UI Library
SkillMediaApply when building or restyling any interface in apps/web, choosing a colour, adding a UI component, or reaching for @vuetify/v0. Esposter's own UI library over Vuetify 0, and the rules an edit follows, only the library imports Vuetify 0, a colour is a token, a feature never names a design style, every list row is UiItemContent, and every unit takes the design pass. Outranks the vendored vuetify0 skill wherever they meet.
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; ahel provides instructions and does not run this skill.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the UI Library skill
What this skill tells your AI
The instructions your AI receives, as published by esposter/esposter in .agents/skills/ui-library/SKILL.md and read by ahel’s review.
Every interface in the app is drawn by a library of its own, built in apps/web on Vuetify 0's headless primitives. How it is built is apps/web/content/docs/architecture/ui-library.md, how it looks is apps/web/content/docs/architecture/design-language.md; this skill is the rules an edit follows.
Settled — do not re-propose
- A styled component library, another headless library, or writing the behaviour ourselves. Argued and rejected in
apps/web/content/docs/architecture/ui-library.md. - Moving the library into a package. It stays in
apps/webuntil a second app consumes it. - Auto-importing Vuetify 0. Its names collide with VueUse's; the library imports it by name, and nothing else imports it at all.
- Keeping the palette in the library's services folder.
uno.config.tsreads it, and loads before any@/alias resolves, so it lives inapps/web/configuration/besidebreakpoints.tsand imports its enums relatively. - Dropping preset-wind4's preflight reset. Every surface assumes its zeroed margins, markers and inherited links; a browser default the library needs is restated where it is needed, as a dialog's margins and a read list's markers are (
apps/web/content/docs/architecture/ui-library.md, "Rejected"). - Voxel as the app's only look. Standard is the default, voxel is the games' look and Genshin is pinned by the agent console; why one look was rejected is the architecture page's "Design styles" section.
A look is a style, not the library
The look is a design style — standard, the default, voxel or Genshin — beside light and dark; the tiers, the selection and what each style draws are the architecture page's "Design styles" section. The rules an edit follows:
- A feature never names a style — it reaches the look through a surface rule, a token or an icon meaning;
useUiStyleanddata-ui-styleare the library's alone (references/design-styles.md). - A drawing no token can express belongs to the library, keyed on
useUiStylewith the same DOM and roles in every style (references/design-styles.md). - A new library component's test runs once per style through
setupUiStyle, and the eye check of a handed-over unit covers both styles in both modes.
Three layers
- A feature uses the library; only the library imports Vuetify 0 — a missing behaviour grows the library first, in a commit of its own (
references/layers.md). - The vendored
vuetify0skill applies inside the library only, and this skill wins where the two disagree (references/layers.md). - Build a component for the feature that consumes it, not in advance. What exists is the architecture page's Components section.
Tokens
How the palette reaches UnoCSS and the first response is apps/web/content/docs/architecture/ui-library.md. The rules an edit follows:
- A colour is a token. A new colour is an entry in
UiPaletteMapfor both themes; a component or a template never writes a hex value or a palette colour the token set already covers. - A length is a whole number of
--ui-step, and a duration is--ui-motion-short,-mediumor-long— never a time, an easing or asteps()of its own (references/tokens.md). - Type is the library's type rules —
ui-bodyon a page's root,ui-heading,ui-titleorui-displayon a heading, and never a font family, size or weight of the template's own (references/tokens.md). - Adding a palette pair or a style token, or selecting a theme, follows
references/tokens.md: a pair that fails the palette test is re-picked, onlyNuxtThemeselects, and a drawing value is a style token.
Icons
A library component and a menu's action name an icon by meaning — UiIconMeaning through UiIconMap, a glyph in every style — and a set's own class never appears in a feature. One meaning, one mark: a state a meaning already names is drawn with that meaning in every control, never a shape of the control's own — a checkbox's tick is Success — and drawn once: a row whose checkbox already says it is done draws no second tick beside its text. Adding a meaning, writing a class whole where one is unavoidable, the // @unocss-include a .ts file needs and finding an icon in a test are references/icons.md.
Components
What exists, what each is built on and its keyboard contract are the architecture page's Components section. The rules an edit follows:
- A card is not a row: what a reader chooses whole is a
ui-card, andui-item/ui-roware for a list or a menu (references/surfaces.md). - Every list row is
UiItemContentinside aui-item, or aui-rowwhere it goes nowhere, and leads with a mark (references/surfaces.md). - A new component comes with a component test of its keyboard and ARIA contract, mounted with the real Vuetify 0 parts rather than mocked ones. A feature's test never re-tests them.
- A surface is a rule, not a style block — a component wears
ui-frame,ui-raised,ui-fieldand the rest fromuno.config.ts(references/surfaces.md). - Anything pressed wears
ui-button—UiButton,UiButtonLinkfor somewhere to go,UiInlineActioninside a sentence — and lays out its own content (references/buttons.md). - A utility cannot recolour a surface; a state that recolours one is a data attribute the component's scoped style reads (
references/surfaces.md). - A search is
UiTextFieldwith the search type, and a field draws no line, ring, shade or padding of the feature's own (references/fields.md). - A field is
UiTextFieldinside aUiForm, with its rules fromUiRulesor aUiRule(references/fields.md). - A choice between views of one thing is
UiTabs; tabs that go somewhere areUiTabLinks(references/tabs.md). - The call site's attributes go on top of the primitive's — render from the attribute slot with
$attrsspread after (references/authoring-components.md). - A popover's open state is the ref handed to
usePopover, never a mirror of the one it returns — no watcher between the two, and a derived one is acomputedwith anoopsetter:references/popovers.md. - A field's completions are
UiSuggestionsbeside the field, never a menu. Focus stays in the field; a menu moves focus into itself and is for actions. - A menu item is data — a
UiMenuItemlist, a command under way disabled rather than hidden (references/menus.md). - A form from a Zod schema is
UiSchemaForm; its schemas' rules arereferences/schema-forms.md. - A key a surface handles — a shortcut, a grid's arrows, a right-click, a search of its own — goes through the library's one mechanism for it, never a listener of the feature's own; which mechanism is
references/keys-and-commands.md. - A write's confirmation, a modal and a toast are the library's, each answering the write it is handed; how a dialog closes on a write, what a modal's content can open, and the one toast stack are
references/dialogs-and-toasts.md. - A name shown on hover is a
UiTooltip, never atitleattribute, and only on a control with no visible text; it joins other bindings in onemergeProps(activatorProps, …)(references/tooltips.md). - A date is
UiDateField, a span of daysUiDateRangeField, and events in timeUiEventCalendar— what they are built on and what a call site hands them:references/dates.md. - A button whose write waits on the server says so through
is-pending, never a hand-drawn spinner or a flag folded into:disabled(references/buttons.md). - A list a read fills is loading until a read settles, never empty — gated on the read's own state, never on
items.length === 0(references/loading-states.md). - A fixed region starts past the dock: subtract
--dock-inset-inline-startand--dock-inset-block-end, never a bar's height. - A page that scrolls inside its own regions passes
is-viewport-heightto its layout; every other page scrolls the window (references/app-shell.md). - A page with a drawer places
AppDrawerButtonin its header — the one way a narrow screen opens a drawer (references/app-shell.md). - A page that is one of a kind of thing declares its mark with
usePageMarkwhile mounted (references/app-shell.md).
The document chrome
Scrollbars, selection, the caret, native accents and the focus ring live once in the ui-chrome layer, and a region in another theme is a UiThemeScope (references/document-chrome.md).
The design pass
A unit is designed, not repainted. Before a unit or a library component is built, and again before it is handed over, walk references/design-pass.md: motion on whatever appears or disappears, placement from what the surface is for, the full width used, one thing to do first, every state designed, a signature detail drawn through the library rather than by hand, and a reason it beats the reference product rather than only matching it.
Redesigning a unit
A unit's flows and states are inventoried before its template is touched, and it fails if the new version drops one; the procedure, the licence to redesign across pages and the ledger trailer are references/redesigning-a-unit.md.
Reference pages
references/design-styles.md— when a look differs by style, or a library component draws what no token can say.references/layers.md— when a feature needs a behaviour the library lacks, or the vendoredvuetify0skill meets a feature.references/surfaces.md— when choosing a card or a row, drawing a list row, or dressing or recolouring a surface.references/buttons.md— when adding anything pressed, or a button whose write waits on the server.references/fields.md— when adding a search, a text field or a form.references/tabs.md— when a surface offers views of one thing, tabs that navigate, or a collapsible group.references/authoring-components.md— when a library component overrides a call site's attributes or exposes its element.references/menus.md— when building a menu's, an overflow button's or a context menu's items.references/tooltips.md— when a control needs a name on hover, or it is unclear whether it takes one.references/loading-states.md— when a list or a panel is filled by a read.references/app-shell.md— when a page scrolls inside itself, has a drawer, or declares its mark for the dock.references/document-chrome.md— when styling scrollbars, selection, the caret, native accents or focus, or theming a region.references/tokens.md— when adding a colour pair, a style token or a theme, or selecting a theme.references/icons.md— when adding an icon, a meaning or an icon set's class, or finding an icon in a test.references/popovers.md— when a component opens a panel throughusePopover, or wires its open state to a parent.references/schema-forms.md— when writing a schema aUiSchemaFormrenders, or the dialog that renders one.references/keys-and-commands.md— when a surface binds a key, walks a grid, opens a context menu or searches something of its own.references/dialogs-and-toasts.md— when a write asks before it acts, a dialog answers a write, or a surface shows a toast.references/dates.md— when a field takes a date or a page shows events in time.references/design-pass.md— before building or redesigning a unit or a library component, and before handing it over.references/redesigning-a-unit.md— when redesigning a product area's unit on the library.
Signals
- GitHub stars
- 23
- Forks
- 3
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
ui-library- Source
- github.com/esposter/esposter