@@ -15,9 +15,9 @@ user-invocable: true
1515- Installing frontend packages used only by custom AdminForth Vue code.
1616- Any task that produces visible UI in an AdminForth app, even when the task says nothing about how it should look.
1717
18- ## Non-Negotiable UI Defaults
18+ ## Adminforth UI Defaults
1919
20- Apply all of these to every piece of UI you write under `custom/`, including when the user gave no design
20+ Apply all of these to every piece of UI you write under `custom/`, especially when the user gave no design
2121instructions at all. These are the defaults, not options — do not ask whether the user wants them, and do
2222not wait for a follow-up prompt about styling or dark mode.
2323
@@ -26,145 +26,32 @@ not wait for a follow-up prompt about styling or dark mode.
2626 component covers the case.
27272. **Buttons come from AFCL with an explicit intent.** Primary/confirming action is the default filled
2828 accent `<Button >`. Secondary, cancel, and "back" actions are stroked `<Button variant =" secondary" >`.
29- Destructive actions are `<Button variant =" danger" >`. Never hand-roll a `<button >` with your own
30- background classes.
29+ Destructive actions are `<Button variant =" danger" >`.
31303. **Form controls come from AFCL.** `Input`, `Textarea`, `Select`, `Checkbox`, `Toggle`, `DatePicker`,
32- `Dropzone`. Never a bare `<input >`, `<select >`, or `<textarea >` styled by hand — that is the main way
31+ `Dropzone`. Preferably not a bare `<input >`, `<select >`, or `<textarea >` styled by hand — that is the main way
3332 custom pages end up looking foreign.
34334. **Accents use `lightPrimary` / `darkPrimary`.** Anything that carries brand or "this is the important
3534 one" meaning — accent fills, highlighted values, active states, links, focus emphasis, the main chart
3635 series — should use `bg-lightPrimary dark:bg-darkPrimary`, `text-lightPrimary dark:text-darkPrimary`,
37- `text-lightPrimaryContrast dark:text-darkPrimaryContrast` on top of an accent fill, or
38- `bg-lightPrimaryOpacity dark:bg-darkPrimaryOpacity` for a subtle tint. Hardcoding an accent
39- (`bg-blue-600`, `text-indigo-500`) breaks apps whose theme sets a different brand color.
36+ `text-lightPrimaryContrast dark:text-darkPrimaryContrast`.
40375. **Everything else may use Tailwind's stock palette.** `bg-white`, `bg-gray-50`, `text-gray-700`,
4138 `text-red-600`, `border-gray-200`, `bg-pink-500`, and friends are all fine for neutrals, surfaces,
4239 borders, and semantic colors. The theme tokens in the table below are still the better choice when a
4340 block sits directly next to built-in AdminForth chrome and should match it exactly — but they are a
4441 recommendation, not a restriction.
45426. **Dark theme is part of writing the class, not a later pass.** Every color utility must be written as a
46- light/dark pair: `bg-white dark:bg-gray-900`, `text-gray-700 dark:text-gray-300`,
47- `bg-lightForm dark:bg-darkForm`. This matters most with stock Tailwind colors, which have no built-in
43+ light/dark pair: `bg-white dark:bg-gray-900`, `text-gray-700 dark:text-gray-300`.
44+ This matters most with stock Tailwind colors, which have no built-in
4845 dark behavior — a `bg-gray-50` with no `dark:` counterpart is a defect, fix it before finishing.
4946 `light*`/`dark*` token pairs satisfy this by construction. Dark mode is class-based
5047 (`darkMode: 'class'`), so `dark:` variants work everywhere in `custom/`.
51- 7. **Non-color utilities are unrestricted.** Tailwind spacing, sizing, radius, flex/grid, and font-size
52- utilities are fine and encouraged. Match AFCL's own rhythm so custom blocks sit naturally next to
53- built-in ones: `rounded-lg`, `text-sm`, `p-4`, `gap-2`/`gap-4`.
54- 8. **Icons come from the prerendered Iconify packages** already present in the SPA:
48+ 7. **Icons come from the prerendered Iconify packages** already present in the SPA:
5549 `@iconify-prerendered/vue-flowbite` (default), plus `-heroicons`, `-humbleicons`, and `-flag`.
5650 Do not add an icon dependency to `custom/package.json` for these.
57- 9 . **Never build Tailwind class names dynamically.** `custom/` is copied into the SPA sources and scanned
51+ 8 . **Never build Tailwind class names dynamically.** `custom/` is copied into the SPA sources and scanned
5852 statically by Tailwind, so `` `text-${color}-600` `` produces no CSS. Write full class strings and pick
5953 between them.
6054
61- ## AFCL Component Inventory
62-
63- All of these are imported from `@/afcl` and are already theme-aware and dark-mode-ready:
64-
65- ```ts
66- import { Button, LinkButton, ButtonGroup, Link, Input, Textarea, Select, Checkbox, Toggle,
67- DatePicker, Dropzone, Card, Table, Dialog, Modal, Tooltip, VerticalTabs,
68- ProgressBar, Spinner, Skeleton, JsonViewer, CountryFlag,
69- AreaChart, BarChart, PieChart, MixedChart, TreeMapChart } from '@/afcl';
70- ```
71-
72- - Actions: `Button` (`variant`: `primary` | `secondary` | `danger`; also `loader`, `disabled`, `active`,
73- `shadow`), `LinkButton` (same variants, navigates via `to`), `ButtonGroup`, `Link`.
74- - Inputs: `Input` (requires `type`, supports `v-model`, `fullWidth`, `readonly`, `prefix`/`suffix` props
75- or slots), `Textarea`, `Select` (`:options="[{ label, value }]"`, `multiple`, `placeholder`),
76- `Checkbox`, `Toggle`, `DatePicker`, `Dropzone`.
77- - Layout and data: `Card`, `Table` (client or server-side data, sorting, pagination), `VerticalTabs`,
78- `Modal`, `Dialog`, `Tooltip`.
79- - Feedback: `Spinner`, `Skeleton`, `ProgressBar`.
80- - Charts: `AreaChart`, `BarChart`, `PieChart`, `MixedChart`, `TreeMapChart` — prefer these over pulling in
81- a new charting library, they already follow theme colors.
82-
83- Before writing a bespoke widget, check this list. Reusing an AFCL component is always the preferred
84- answer to "make a button / input / table / modal / chart".
85-
86- ## Theme Color Tokens
87-
88- These light/dark pairs follow the app's configured theme automatically. The accent rows are the ones you
89- should actually reach for by default (rule 4); the rest are available when you want a custom block to
90- match built-in AdminForth chrome pixel-for-pixel instead of approximating it with stock grays. Every
91- `light*` token has a `dark*` twin.
92-
93- | Need | Classes |
94- | --- | --- |
95- | **Accent (brand) text** | `text-lightPrimary dark:text-darkPrimary` |
96- | **Accent fill** | `bg-lightPrimary dark:bg-darkPrimary` |
97- | **Text on accent fill** | `text-lightPrimaryContrast dark:text-darkPrimaryContrast` |
98- | **Subtle accent tint** | `bg-lightPrimaryOpacity dark:bg-darkPrimaryOpacity` |
99- | Panel / form surface | `bg-lightForm dark:bg-darkForm` |
100- | Panel border | `border-lightFormBorder dark:border-darkFormBorder` |
101- | Section heading strip | `bg-lightFormHeading dark:bg-darkFormHeading` |
102- | Card surface | `bg-lightCardBackground dark:bg-darkCardBackground` |
103- | Card border | `border-lightCardBorder dark:border-darkCardBorder` |
104- | Strong / title text | `text-lightCardTitle dark:text-darkCardTitle` |
105- | Muted / secondary text | `text-lightCardDescription dark:text-darkCardDescription` |
106- | Body and table text | `text-lightListTableText dark:text-darkListTableText` |
107- | Table heading text | `text-lightListTableHeadingText dark:text-darkListTableHeadingText` |
108- | Divider / separator | `border-lightListBorder dark:border-darkListBorder` |
109- | Error / invalid text | `text-lightInputErrorColor dark:text-darkInputErrorColor` |
110- | Required marker | `text-lightRequiredIconColor dark:text-darkRequiredIconColor` |
111- | Focus ring | `focus:ring-lightFocusRing dark:focus:ring-darkFocusRing` |
112-
113- The complete token list lives in `node_modules/adminforth/dist/modules/styles.js`. Look a name up there
114- instead of inventing one — an unknown token silently produces no CSS.
115-
116- ## Default Panel Recipe
117-
118- When a task asks for "a panel", "a summary block", "a small form", or any custom page area with no visual
119- spec, start from this shape. It is theme-correct and dark-ready with no extra work:
120-
121- ```vue
122- <template >
123- <div class =" rounded-lg border border-lightFormBorder dark:border-darkFormBorder
124- bg-lightForm dark:bg-darkForm p-4" >
125- <h3 class =" text-lg font-semibold text-lightCardTitle dark:text-darkCardTitle" >
126- \{{ meta ?.title || ' Orders overview' }}
127- </h3 >
128- <p class =" mt-1 text-sm text-lightCardDescription dark:text-darkCardDescription" >
129- Totals for the current filter
130- </p >
131-
132- <div class =" mt-4 grid gap-3 sm:grid-cols-2" >
133- <Input type =" text" v-model =" query" full-width placeholder =" Search orders" />
134- <Select v-model =" period" :options =" periodOptions" placeholder =" Period" />
135- </div >
136-
137- <div class =" mt-4 flex items-center gap-2" >
138- <Button :loader =" loading" @click =" apply" >Apply</Button >
139- <Button variant =" secondary" @click =" reset" >Reset</Button >
140- </div >
141-
142- <p v-if =" error" class =" mt-2 text-sm text-lightInputErrorColor dark:text-darkInputErrorColor" >
143- \{{ error }}
144- </p >
145- </div >
146- </template >
147-
148- <script setup lang =" ts" >
149- import { ref } from ' vue' ;
150- import { Button , Input , Select } from ' @/afcl' ;
151-
152- defineProps< { meta?: { title?: string } }> ();
153-
154- const query = ref (' ' );
155- const period = ref (null );
156- const loading = ref (false );
157- const error = ref (' ' );
158- const periodOptions = [
159- { label: ' Last 7 days' , value: ' 7' },
160- { label: ' Last 30 days' , value: ' 30' },
161- ];
162-
163- function apply () { /* ... */ }
164- function reset () { query .value = ' ' ; period .value = null ; }
165- </script >
166- ```
167-
16855## Dark Theme Self-Check
16956
17057Run this over every file you touched before reporting the work as done:
@@ -177,8 +64,6 @@ Run this over every file you touched before reporting the work as done:
17764 dark mode.
17865- Accents are `lightPrimary`/`darkPrimary`, not a hardcoded blue or indigo.
17966- No raw `#hex` or `rgb()` in templates or `<style >` blocks.
180- - Any raw `<button > `, `<input > `, `<select > `, or `<textarea > ` has a justification; otherwise replace it with
181- the AFCL equivalent.
18267
18368```
18469❌ <button class="bg-blue-600 text-white rounded px-4 py-2 "> Save</button >
@@ -187,14 +72,8 @@ Run this over every file you touched before reporting the work as done:
18772❌ <div class="bg-white border border-gray-200 text-gray-800 ">
18873✅ <div class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700
18974 text-gray-800 dark:text-gray-200 ">
190- ✅ <div class="bg-lightForm dark:bg-darkForm border border-lightFormBorder
191- dark:border-darkFormBorder text-lightListTableText dark:text-darkListTableText">
192-
19375❌ <p class="text-red-600 "> \{ { error }}</p >
19476✅ <p class="text-red-600 dark:text-red-400 "> \{ { error }}</p >
195-
196- ❌ <span class="font-semibold text-blue-600 dark:text-blue-400 "> \{ { total }}</span >
197- ✅ <span class="font-semibold text-lightPrimary dark:text-darkPrimary"> \{ { total }}</span >
19877```
19978
20079## `custom/` Directory and `@@/`
@@ -322,11 +201,11 @@ show: {
322201 @update:model-value ="onInput"
323202 />
324203
325- <p v-if ="errorMessage" class="text-sm text-lightInputErrorColor dark:text-darkInputErrorColor ">
204+ <p v-if ="errorMessage" class="text-sm ">
326205 \{ { errorMessage }}
327206 </p >
328207
329- <p v-else-if ="isEmpty" class="text-sm text-lightCardDescription dark:text-darkCardDescription ">
208+ <p v-else-if ="isEmpty" class="text-sm ">
330209 Value is currently empty
331210 </p >
332211 </div >
@@ -414,7 +293,7 @@ function syncState() {
414293
415294```vue
416295<template >
417- <div class="flex items-center gap-2 text-lightListTableText dark:text-darkListTableText ">
296+ <div class="flex items-center gap-2 ">
418297 <span >
419298 \{ { meta ?.filler ?.repeat (record .number _of _rooms || 0) }}
420299 </span >
@@ -530,6 +409,4 @@ options: {
530409- Reach for an AFCL component before writing markup; use `lightPrimary`/`darkPrimary` for accents; write
531410 the `dark:` variant in the same edit as the light one. These are defaults for every UI task, not polish
532411 to be added when someone asks for it.
533- - Stock Tailwind colors are fine for neutrals and semantic states — just never leave one without its
534- `dark:` counterpart.
535412- When you are done, re-read your diff against the Dark Theme Self-Check above before reporting completion.
0 commit comments