KeyComponents
Overviewimport { ChartLegend, ChartLegendDot, ContentCard, ContentLabel, ControlLabel, HintText } from '@nili/ui';This is the library’s own overview page, rendered live. Switch the language or the theme in the header and it follows.
Label
The field label every input uses. The asterisk and the sublabel are hidden from assistive tech — requiredness is carried by the input's own required, and a screen reader that hears “Phone Number star optional” has been told the same thing three times, twice contradictorily.
import { Label, LinkButton } from "@nili/ui"
export function Example() {
return (
<>
<Label htmlFor="a">Email address</Label>
<Label htmlFor="b" required>Email address</Label>
<Label htmlFor="c" sublabel="(optional)">Email address</Label>
<Label htmlFor="d" information informationLabel="Why we ask">
Email address
</Label>
<Label htmlFor="e" hint={<LinkButton href="#" size="sm">Forgot?</LinkButton>}>
Password
</Label>
<Label htmlFor="f" disabled>Email address</Label>
</>
)
}Hint Text
The line under a field. status colours it; icon adds the matching glyph. The error status is what a Text Input renders when you pass it an error.
This is a hint text to help user.
Please enter a valid email address.
This field is unavailable.
import { HintText } from "@nili/ui"
export function Example() {
return (
<>
<HintText icon>This is a hint text to help user.</HintText>
<HintText status="error" icon>Please enter a valid email.</HintText>
<HintText status="disabled">This field is unavailable.</HintText>
</>
)
}Control Label
The label row a Checkbox, Radio or Switch builds internally — label, sublabel, badge, description and an action. Exported because a custom control should look like the built-in ones.
import { ControlLabel, Badge, LinkButton } from "@nili/ui"
export function Example() {
return (
<ControlLabel
htmlFor="notify"
label="Email notifications"
sublabel="(recommended)"
badge={<Badge size="small" color="green" appearance="lighter">Free</Badge>}
description="We will only email you about things you asked for."
linkButton={<LinkButton href="#" size="sm">Manage</LinkButton>}
/>
)
}Password Strength
Four levels. The meter is decorative on its own — pass the label so the verdict is written out, not left to a colour.
import { PasswordStrength, TextInput } from "@nili/ui"
export function Example() {
return (
<>
<TextInput type="password" label="Password" />
<PasswordStrength strength="moderate" label="Could be stronger" />
</>
)
}Key Icon
The framed icon that heads a modal, an empty state or a content card. Two appearances, ten colours, five sizes.
import { KeyIcon, Icon } from "@nili/ui"
import { RiSparkling2Line } from "@remixicon/react"
export function Example() {
return (
<>
<KeyIcon color="purple"><Icon icon={RiSparkling2Line} /></KeyIcon>
<KeyIcon color="purple" appearance="stroke"><Icon icon={RiSparkling2Line} /></KeyIcon>
<KeyIcon color="purple" size="2xl"><Icon icon={RiSparkling2Line} /></KeyIcon>
</>
)
}Chart Legend
With onToggle it renders as a button with aria-pressed, because toggling a series is a control. Without it, plain text. value is pre-formatted — numerals are a locale decision, not the legend's.
import { ChartLegend, ChartLegendDot } from "@nili/ui"
export function Example() {
const [hidden, setHidden] = useState<string[]>([])
return (
<>
{series.map((item) => (
<ChartLegend
key={item.key}
color={item.color}
value={item.value}
hidden={hidden.includes(item.key)}
onToggle={() => toggle(item.key)}
>
{item.label}
</ChartLegend>
))}
{/* just the dot */}
<ChartLegendDot color="blue" />
</>
)
}Content Label and Content Card
A title with an optional leading and trailing slot. Figma draws six Types — avatar, icon, brand, company — and they are all the same startAdornment. Content Card is the selectable version.
import { ContentLabel, ContentCard, KeyIcon, Avatar, Icon, Badge } from "@nili/ui"
import { RiFolderLine } from "@remixicon/react"
export function Example() {
const [selected, setSelected] = useState("a")
return (
<>
<ContentLabel
startAdornment={<Avatar size="sm" name="Ada Lovelace" tone="blue" />}
title="Ada Lovelace"
description="Owner · ada@nili.design"
/>
<ContentCard
selected={selected === "a"}
onSelect={() => setSelected("a")}
startAdornment={
<KeyIcon size="md" color="blue"><Icon icon={RiFolderLine} /></KeyIcon>
}
title="Design system"
description="24 components · updated 2 days ago"
badge={<Badge size="small" color="green" appearance="lighter">v0.2</Badge>}
/>
</>
)
}ChartLegend
extends Omit< ComponentPropsWithoutRef<'button'>, 'color' | 'disabled' | 'value' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| disabled | boolean | — | |
| required | boolean | — | |
| readOnly | boolean | — | |
| value | ReactNode | — | Trailing figure — pre-formatted, since numerals are a locale decision. |
| defaultValue | string | — | |
| name | string | — | |
| aria-label | string | — | |
| color | yellow | blue | green | pink | purple | red | gray | orange | sky | teal | disabled | light-gray | blue | |
| size | md | sm | md | |
| hidden | boolean | false | Series is hidden. Renders `aria-pressed` so the state is announced. |
| onToggle | () => void | — | Clicking toggles the series. Without it, renders as plain text. |
ChartLegendDot
extends Omit< ComponentPropsWithoutRef<'span'>, 'color' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| color | yellow | blue | green | pink | purple | red | gray | orange | sky | teal | disabled | light-gray | blue | |
| size | md | sm | md |
ContentCard
extends ContentLabelProps
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| onSelect | () => void | — | Renders a `<button>` so the whole card is keyboard reachable. |
| selected | boolean | false | |
| startAdornment | ReactNode | — | Flag, icon, avatar, brand mark — all five Figma Types land here. |
| endAdornment | ReactNode | — | |
| title* | ReactNode | — | |
| sublabel | ReactNode | — | Figma `💬 Sublabel` — the muted run *beside* the title, on the same line. |
| description | ReactNode | — | |
| badge | ReactNode | — | Figma `🎖️ Badge` — sits after the title, before any sublabel. |
| required | boolean | — | Figma `🚨 Required` — the asterisk after the title. Decorative. |
| toggle | ReactNode | — | Figma `🔀 Toggle` — trailing control. Rendered after `endAdornment`. |
| size | lg | md | — |
ContentLabel
extends Omit< ComponentPropsWithoutRef<'div'>, 'title' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| startAdornment | ReactNode | — | Flag, icon, avatar, brand mark — all five Figma Types land here. |
| endAdornment | ReactNode | — | |
| title* | ReactNode | — | |
| sublabel | ReactNode | — | Figma `💬 Sublabel` — the muted run *beside* the title, on the same line. |
| description | ReactNode | — | |
| badge | ReactNode | — | Figma `🎖️ Badge` — sits after the title, before any sublabel. |
| required | boolean | false | Figma `🚨 Required` — the asterisk after the title. Decorative. |
| toggle | ReactNode | — | Figma `🔀 Toggle` — trailing control. Rendered after `endAdornment`. |
| size | lg | md | md |
ControlLabel
extends Omit< ComponentPropsWithoutRef<'span'>, 'children' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| htmlFor | string | — | Ties the label to its control. Omit when the label is not a `<label>`. |
| label | ReactNode | — | |
| sublabel | ReactNode | — | Muted text sitting inline *after* the label — "(optional)", a unit, a count. Figma's `Sublabel`. Distinct from `description`, which is a second line rather than a continuation of the first. |
| description | ReactNode | — | Second line under the label. Figma's `Description`. |
| badge | ReactNode | — | Trailing slot on the label row — a `<Badge>`. |
| linkButton | ReactNode | — | Action under the description — a `<LinkButton>`. |
| disabled | boolean | false | |
| descriptionId | string | — | Wired to the control's `aria-describedby`. |
HintText
extends ComponentPropsWithoutRef<'p'>, VariantProps<typeof hintTextVariants>
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| icon | boolean | ReactNode | false | `true` shows the default glyph for the status; a node overrides it. |
KeyIcon
extends Omit< ComponentPropsWithoutRef<'span'>, 'color' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children* | ReactNode | — | |
| aria-label | string | — | |
| appearance | lighter | stroke | lighter | |
| color | yellow | blue | green | pink | purple | red | gray | orange | sky | primary | primary | |
| size | 2xl | xl | lg | md | sm | lg | |
| label | string | — | Provide only when the icon carries meaning alone. Usually it does not. |
Label
extends ComponentPropsWithoutRef<'label'>, VariantProps<typeof labelVariants>
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| required | boolean | false | Adds an asterisk and, crucially, keeps it out of the accessible name. |
| sublabel | ReactNode | — | Figma `💬 Sublabel` — the muted "(Optional)" after the asterisk. |
| information | boolean | ReactNode | false | Figma `ℹ️ Information` — the glyph that explains the field. |
| informationLabel | string | — | Accessible name for the information glyph. |
| hint | ReactNode | — | Figma `🌟 Button` — trailing slot, usually a LinkButton. |
| rowClassName | string | — | Applied to the row rather than to the `<label>` element. |
PasswordStrength
extends Omit< ComponentPropsWithoutRef<'div'>, 'children' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| strength | strong | empty | weak | moderate | empty | |
| label | string | — | Visible text beside the meter, e.g. "Strong password". |