Fixing issues with field

This commit is contained in:
Ronan Berder
2025-11-04 20:41:23 +08:00
parent f82b1d79d4
commit b8d8b1741c
3 changed files with 131 additions and 153 deletions
+53 -118
View File
@@ -5,7 +5,6 @@
:root, :host {
--font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
--color-red-500: oklch(63.7% 0.237 25.331);
@@ -72,13 +71,6 @@
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--shadow-2xs: 0 1px rgb(0 0 0 / 0.05);
--shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--animate-spin: spin 1s linear infinite;
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
@@ -261,9 +253,6 @@
}
}
@layer utilities {
.\@container {
container-type: inline-size;
}
.pointer-events-none {
pointer-events: none;
}
@@ -302,9 +291,6 @@
.top-0 {
top: calc(var(--spacing) * 0);
}
.top-1 {
top: calc(var(--spacing) * 1);
}
.top-1\/2 {
top: calc(1/2 * 100%);
}
@@ -314,15 +300,9 @@
.top-22 {
top: calc(var(--spacing) * 22);
}
.right-1 {
right: calc(var(--spacing) * 1);
}
.right-1\.5 {
right: calc(var(--spacing) * 1.5);
}
.right-2 {
right: calc(var(--spacing) * 2);
}
.right-2\.5 {
right: calc(var(--spacing) * 2.5);
}
@@ -332,9 +312,6 @@
.bottom-0 {
bottom: calc(var(--spacing) * 0);
}
.left-1 {
left: calc(var(--spacing) * 1);
}
.left-1\.5 {
left: calc(var(--spacing) * 1.5);
}
@@ -416,9 +393,6 @@
.mb-12 {
margin-bottom: calc(var(--spacing) * 12);
}
.-ml-1 {
margin-left: calc(var(--spacing) * -1);
}
.-ml-1\.5 {
margin-left: calc(var(--spacing) * -1.5);
}
@@ -571,30 +545,18 @@
.w-0 {
width: calc(var(--spacing) * 0);
}
.w-1 {
width: calc(var(--spacing) * 1);
}
.w-1\/2 {
width: calc(1/2 * 100%);
}
.w-2 {
width: calc(var(--spacing) * 2);
}
.w-2\/3 {
width: calc(2/3 * 100%);
}
.w-3 {
width: calc(var(--spacing) * 3);
}
.w-3\/4 {
width: calc(3/4 * 100%);
}
.w-4 {
width: calc(var(--spacing) * 4);
}
.w-7 {
width: calc(var(--spacing) * 7);
}
.w-7\.5 {
width: calc(var(--spacing) * 7.5);
}
@@ -697,29 +659,14 @@
.shrink-0 {
flex-shrink: 0;
}
.border-collapse {
border-collapse: collapse;
}
.-translate-x-1 {
--tw-translate-x: calc(var(--spacing) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.-translate-x-1\/2 {
--tw-translate-x: calc(calc(1/2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.-translate-y-1 {
--tw-translate-y: calc(var(--spacing) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.-translate-y-1\/2 {
--tw-translate-y: calc(calc(1/2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.translate-y-0 {
--tw-translate-y: calc(var(--spacing) * 0);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.translate-y-0\.5 {
--tw-translate-y: calc(var(--spacing) * 0.5);
translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -736,9 +683,6 @@
.cursor-pointer {
cursor: pointer;
}
.resize {
resize: both;
}
.scroll-m-20 {
scroll-margin: calc(var(--spacing) * 20);
}
@@ -793,9 +737,6 @@
.justify-start {
justify-content: flex-start;
}
.gap-0 {
gap: calc(var(--spacing) * 0);
}
.gap-0\.5 {
gap: calc(var(--spacing) * 0.5);
}
@@ -857,9 +798,6 @@
margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)));
}
}
.gap-x-1 {
column-gap: calc(var(--spacing) * 1);
}
.gap-x-1\.5 {
column-gap: calc(var(--spacing) * 1.5);
}
@@ -1045,9 +983,6 @@
.bg-transparent {
background-color: transparent;
}
.mask-repeat {
mask-repeat: repeat;
}
.object-cover {
object-fit: cover;
}
@@ -1099,9 +1034,6 @@
.px-\[0\.3rem\] {
padding-inline: 0.3rem;
}
.py-1 {
padding-block: calc(var(--spacing) * 1);
}
.py-1\.5 {
padding-block: calc(var(--spacing) * 1.5);
}
@@ -1117,9 +1049,6 @@
.py-\[0\.2rem\] {
padding-block: 0.2rem;
}
.pt-1 {
padding-top: calc(var(--spacing) * 1);
}
.pt-1\.5 {
padding-top: calc(var(--spacing) * 1.5);
}
@@ -1275,9 +1204,6 @@
.text-balance {
text-wrap: balance;
}
.text-wrap {
text-wrap: wrap;
}
.break-words {
overflow-wrap: break-word;
}
@@ -1975,11 +1901,6 @@
}
}
}
.\[\&_\.text-muted-foreground\]\:hidden {
& .text-muted-foreground {
display: none;
}
}
.\[\&_\[data-name\]\]\:hidden {
& [data-name] {
display: none;
@@ -2142,11 +2063,6 @@
padding-top: calc(var(--spacing) * 2);
}
}
.\[\&_span\]\:hidden {
& span {
display: none;
}
}
.\[\&_svg\]\:pointer-events-none {
& svg {
pointer-events: none;
@@ -2201,11 +2117,6 @@
padding-left: calc(var(--spacing) * 4);
}
}
.\[\&\.text-muted-foreground\]\:hidden {
&.text-muted-foreground {
display: none;
}
}
.md\:\*\:\[\.card\]\:basis-1\/4 {
@media (width >= 48rem) {
:is(& > *) {
@@ -2234,16 +2145,6 @@
}
}
}
.\[\&\>\.text-muted-foreground\]\:hidden {
&>.text-muted-foreground {
display: none;
}
}
.\[\&\>\[data-name\]\]\:hidden {
&>[data-name] {
display: none;
}
}
.\[\&\>a\]\:underline {
&>a {
text-decoration-line: underline;
@@ -2259,16 +2160,6 @@
color: var(--color-primary);
}
}
.\[\&\>em\]\:hidden {
&>em {
display: none;
}
}
.\[\&\>span_\.text-muted-foreground\]\:hidden {
&>span .text-muted-foreground {
display: none;
}
}
.\[\&\>svg\]\:mb-3 {
&>svg {
margin-bottom: calc(var(--spacing) * 3);
@@ -3666,9 +3557,7 @@
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
& + p {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
color: var(--color-muted-foreground);
margin-top: calc(var(--spacing) * -1.5);
}
}
}
@@ -3686,17 +3575,63 @@
&>.sr-only {
width: auto;
}
.description {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
color: var(--color-muted-foreground);
}
.error {
h2, h3 {
display: flex;
width: fit-content;
align-items: center;
gap: calc(var(--spacing) * 2);
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
--tw-leading: var(--leading-snug);
line-height: var(--leading-snug);
--tw-font-weight: var(--font-weight-medium);
font-weight: var(--font-weight-medium);
}
[role="alert"] {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
--tw-font-weight: var(--font-weight-normal);
font-weight: var(--font-weight-normal);
color: var(--color-destructive);
ul {
margin-left: calc(var(--spacing) * 4);
display: flex;
list-style-type: disc;
flex-direction: column;
gap: calc(var(--spacing) * 1);
}
}
section {
display: flex;
flex: 1;
flex-direction: column;
gap: calc(var(--spacing) * 1.5);
--tw-leading: var(--leading-snug);
line-height: var(--leading-snug);
}
}
.fieldset legend + p, .field p {
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
--tw-leading: var(--leading-normal);
line-height: var(--leading-normal);
--tw-font-weight: var(--font-weight-normal);
font-weight: var(--font-weight-normal);
color: var(--color-muted-foreground);
&:last-child {
margin-top: calc(var(--spacing) * 0);
}
&:nth-last-child(2) {
margin-top: calc(var(--spacing) * -1);
}
&>a {
text-decoration-line: underline;
}
&>a {
text-underline-offset: 4px;
}
&>a:hover {
color: var(--color-primary);
}
}
}
+61 -30
View File
@@ -27,6 +27,11 @@ toc:
{% from "macros/code_preview.njk" import code_preview %}
{% from "macros/code_block.njk" import code_block %}
<div class="alert-destructive">
{% lucide "triangle-alert"%}
<h2>This component is still under development, API may change.</h2>
</div>
{% set code_demo %}
<form class="w-full max-w-md space-y-6">
<fieldset class="fieldset">
@@ -41,7 +46,7 @@ toc:
<div role="group" class="field">
<label for="card-number">Card Number</label>
<input id="card-number" type="text" placeholder="1234 5678 9012 3456" aria-describedby="card-number-desc" required>
<p id="card-number-desc" class="description">Enter your 16-digit card number</p>
<p id="card-number-desc">Enter your 16-digit card number</p>
</div>
<div class="grid grid-cols-3 gap-4">
@@ -115,18 +120,44 @@ toc:
<h2 id="usage"><a href="#usage">Usage</a></h2>
<section class="prose">
<p>Field provides semantic HTML patterns for accessible forms:</p>
<p>Field uses semantic HTML with minimal classes. Two main patterns can be used independently or composed together:</p>
<ul>
<li><code>&lt;fieldset&gt;</code> and <code>&lt;legend&gt;</code> for grouping related fields</li>
<li><code>&lt;p&gt;</code> after <code>&lt;legend&gt;</code> is automatically styled as description text</li>
<li><code>role="group"</code> with <code>.field</code> class for individual field containers</li>
<li><code>.description</code> class for field-level helper text (requires <code>aria-describedby</code>)</li>
<li><code>.error</code> class with <code>role="alert"</code> for error messages</li>
<li><code>&lt;div role="group" class="field"&gt;</code> for individual field containers (label, input, helper text, errors)</li>
<li><code>&lt;fieldset class="fieldset"&gt;</code> for grouping multiple related fields together</li>
</ul>
<p>Elements inside these containers are styled automatically based on their purpose. Use <code>aria-describedby</code> to connect helper text and errors to inputs for accessibility.</p>
<p><strong>Fields</strong> have the following HTML structure:</p>
<dl>
<dt><code class="highlight language-html">&lt;div role="group" class="field"&gt;</code></dt>
<dd>Field container.
<dl>
<dt><code class="highlight language-html">&lt;label&gt;</code></dt>
<dd>Label for the input.</dd>
<dt><code class="highlight language-html">&lt;input&gt;</code>, <code>&lt;select&gt;</code> or <code>&lt;textarea&gt;</code></dt>
<dd>Form input.</dd>
<dt><code class="highlight language-html">&lt;p&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>Helper text. Use <code>aria-describedby</code> on the input to reference this element's <code>id</code>.</dd>
<dt><code class="highlight language-html">&lt;p role="alert"&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>Error message. Use <code>aria-describedby</code> on the input and <code>aria-invalid="true"</code> to reference this element's <code>id</code>.</dd>
</dl>
</dd>
</dl>
<p><strong>Fieldsets</strong> have the following HTML structure:</p>
<dl>
<dt><code class="highlight language-html">&lt;fieldset class="fieldset"&gt;</code></dt>
<dd>Container for grouping related fields.
<dl>
<dt><code class="highlight language-html">&lt;legend&gt;</code></dt>
<dd>Heading for the fieldset.</dd>
<dt><code class="highlight language-html">&lt;p&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>Description text. When placed directly after <code>&lt;legend&gt;</code>, automatically styled as muted text.</dd>
<dt><code class="highlight language-html">&lt;div role="group" class="field"&gt;</code> <span class="badge-secondary">Optional</span></dt>
<dd>Individual field containers (see structure above). You can include multiple <code>.field</code> elements, or place form controls directly in the fieldset.</dd>
</dl>
</dd>
</dl>
</section>
{{ code_block(code) }}
<h2 id="examples"><a href="#examples">Examples</a></h2>
<h3 id="example-input"><a href="#example-input">Input</a></h3>
@@ -136,12 +167,12 @@ toc:
<div role="group" class="field">
<label for="username">Username</label>
<input id="username" type="text" placeholder="evilrabbit" aria-describedby="username-desc">
<p id="username-desc" class="description">Choose a unique username for your account.</p>
<p id="username-desc">Choose a unique username for your account.</p>
</div>
<div role="group" class="field">
<label for="password">Password</label>
<p id="password-desc" class="description">Must be at least 8 characters long.</p>
<p id="password-desc">Must be at least 8 characters long.</p>
<input id="password" type="password" placeholder="••••••••" aria-describedby="password-desc">
</div>
</div>
@@ -155,7 +186,7 @@ toc:
<div role="group" class="field">
<label for="bio">Feedback</label>
<textarea id="bio" rows="4" placeholder="Your feedback helps us improve..." aria-describedby="bio-desc"></textarea>
<p id="bio-desc" class="description">Share your thoughts about our service.</p>
<p id="bio-desc">Share your thoughts about our service.</p>
</div>
{% endset %}
@@ -177,7 +208,7 @@ toc:
<option value="finance">Finance</option>
<option value="operations">Operations</option>
</select>
<p id="country-desc" class="description">Select your department or area of work.</p>
<p id="country-desc">Select your department or area of work.</p>
</div>
{% endset %}
@@ -188,7 +219,7 @@ toc:
{% set code %}
<div role="group" class="field">
<label for="price-range">Price</label>
<p id="price-range-desc" class="description">Set your budget: $<span id="price-range-value">150</span></p>
<p id="price-range-desc">Set your budget: $<span id="price-range-value">150</span></p>
<input id="price-range" type="range" min="0" max="500" value="150">
</div>
<script>
@@ -233,13 +264,13 @@ toc:
<div role="group" class="field">
<label for="full-name">Full name</label>
<input id="full-name" type="text" placeholder="Evil Rabbit" aria-describedby="name-desc">
<p id="name-desc" class="description">Your first and last name</p>
<p id="name-desc">Your first and last name</p>
</div>
<div role="group" class="field">
<label for="display-name">Username</label>
<input id="display-name" type="text" placeholder="@evilrabbit" aria-invalid="true" aria-describedby="username-error">
<p id="username-error" class="error" role="alert">Choose another username</p>
<p id="username-error" role="alert">Username is already taken</p>
</div>
<div class="flex items-center gap-3">
@@ -293,10 +324,10 @@ toc:
<div class="field">
<div class="flex w-full gap-3 flex-row items-center">
<input id="sync-desktop-documents" type="checkbox" checked class="input self-start">
<div class="flex flex-1 flex-col gap-1.5 leading-snug">
<section>
<label for="sync-desktop-documents">Sync Desktop & Documents folders</label>
<p class="description">Your Desktop & Documents folders are being synced with iCloud Drive. You can access them from other devices.</p>
</div>
<p>Your Desktop & Documents folders are being synced with iCloud Drive. You can access them from other devices.</p>
</section>
</div>
</div>
</fieldset>
@@ -344,10 +375,10 @@ toc:
{% set code_switch %}
<div role="group" class="field flex-row">
<div class="flex flex-1 flex-col gap-1.5 leading-snug">
<section>
<label for="multi-factor-authentication">Multi-factor authentication</label>
<p class="description">Enable multi-factor authentication. If you do not have a two-factor device, you can use a one-time code sent to your email.</p>
</div>
<p>Enable multi-factor authentication. If you do not have a two-factor device, you can use a one-time code sent to your email.</p>
</section>
<input id="multi-factor-authentication" type="checkbox" role="switch">
</div>
{% endset %}
@@ -362,20 +393,20 @@ toc:
<p>Select the compute environment for your cluster.</p>
<div role="group" class="field">
<label class="flex gap-3 border border-transparent rounded-md p-4 has-[:checked]:border-primary has-[:checked]:bg-primary/5 dark:has-[:checked]:bg-primary/10">
<div class="flex flex-1 flex-col gap-1.5 leading-snug">
<label class="flex gap-3 border rounded-md p-4 has-[:checked]:border-primary has-[:checked]:bg-primary/5 dark:has-[:checked]:bg-primary/10">
<section>
<h3>Kubernetes</h3>
<p class="description text-">Run GPU workloads on a K8s configured cluster.</p>
</div>
<p>Run GPU workloads on a K8s configured cluster.</p>
</section>
<input id="choice-card" type="radio" name="choice-card" class="self-start" checked>
</label>
</div>
<div role="group" class="field">
<label class="flex gap-3 border border-transparent rounded-md p-4 has-[:checked]:border-primary has-[:checked]:bg-primary/5 dark:has-[:checked]:bg-primary/10">
<div class="flex flex-1 flex-col gap-1.5 leading-snug">
<label class="flex gap-3 border rounded-md p-4 has-[:checked]:border-primary has-[:checked]:bg-primary/5 dark:has-[:checked]:bg-primary/10">
<section>
<h3>Virtual Machine</h3>
<p class="description text-">Access a VM configured cluster to run GPU workloads.</p>
</div>
<p>Access a VM configured cluster to run GPU workloads.</p>
</section>
<input id="choice-card" type="radio" name="choice-card" class="self-start">
</label>
</div>
+17 -5
View File
@@ -697,19 +697,31 @@
@apply text-base font-medium mb-3;
& + p {
@apply text-sm text-muted-foreground;
@apply -mt-1.5;
}
}
}
.field {
@apply flex flex-col w-full gap-3 data-[invalid=true]:text-destructive [&>*]:w-full [&>.sr-only]:w-auto;
.description {
@apply text-sm text-muted-foreground;
h2,
h3 {
@apply flex w-fit items-center gap-2 text-sm leading-snug font-medium;
}
.error {
@apply text-sm text-destructive font-medium;
[role="alert"] {
@apply text-sm text-destructive font-normal;
ul {
@apply ml-4 flex list-disc flex-col gap-1;
}
}
section {
@apply flex flex-1 flex-col gap-1.5 leading-snug;
}
}
.fieldset legend + p,
.field p {
@apply text-muted-foreground text-sm leading-normal font-normal last:mt-0 nth-last-2:-mt-1 [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4;
}
}