Styling contract
The headless label contract is intentionally small. Style the native label itself, then use the data attributes to attach required or disabled treatment without adding extra wrapper logic.
| Surface | Why it exists |
|---|---|
label[tngLabel][data-slot="label"] | Base selector for layout, typography, gap, and field spacing. |
[data-required] | Append or recolor a required marker in CSS without owning extra markup. |
[data-disabled] | Reduce emphasis on labels that accompany disabled or archived controls. |
State selectors
label.state.css
css
label[tngLabel][data-slot="label"] {
display: inline-flex;
align-items: center;
gap: 0.35rem;
}
label[tngLabel][data-required]::after {
content: "*";
color: var(--tng-semantic-accent-danger, #dc2626);
}
label[tngLabel][data-disabled] {
color: var(--tng-semantic-foreground-secondary, #64748b);
opacity: 0.72;
}
field-shell.css
css
.label-field-card {
display: grid;
gap: 1rem;
inline-size: min(100%, 34rem);
margin-inline: auto;
padding: 1rem;
border: 1px solid var(--tng-semantic-border-subtle, #cbd5e1);
border-radius: 1rem;
background: var(--tng-semantic-background-base, #ffffff);
}
.label-field-card__field {
display: grid;
gap: 0.55rem;
}
Example shells
The label stays native while your surrounding field shell carries the visual language.