AdminForth Design SystemHairline theme · tokens, screens and AFCL components
Accent

Foundations

AdminForth · theme

Hairline foundations

Light gray ground, white surfaces drawn with 1px hairlines, ink actions and a single accent from the app config.

Geist · Geist Mono Lucide 1.75 stroke accent = styles.colors.primary

Color tokens

Every swatch is one custom property; dark values are stepped separately, not inverted.
Light
bg#FAFAFA
surface#FFFFFF
surface-2#F4F4F5
surface-3#ECECEE
border#E4E4E7
border-strong#D4D4D8
faint#A1A1AA
muted#696973
text-2#3F3F46
text · ink#18181B
accent#16537E
accent-soft9% mix
danger#D92D20
success#079455
warning#DC6803
Dark
bg#0D0D0F
surface#151517
surface-2#1D1D20
surface-3#27272B
border#29292E
border-strong#3A3A41
faint#6B6B74
muted#A1A1AA
text-2#D4D4D8
text · ink#F4F4F5
accent-fg45% → white
accent-soft22% mix
danger#E5484D
success#3DD68C
warning#F5A524

Type

Geist, 14px base, tabular numbers in data
22/28 · 600Car - SQLite
16/24 · 600Specifications
14/20 · 400Detailed technical specification sheet for the listing.
13/18 · 400Driftstone Skylark R · $7,180 · 31,050 km
12/16 · 500Showing 1–12 of 248 entries
Mono 12.5e2d8a710 · 2026-10-13T09:41:00Z

Radius & elevation

Flat by default — shadows only for floating layers
4 · check
6 · control
8 · card
12 · dialog
full · badge
hairline · cards, tables
xs · inputs, buttons
pop · menus, dialogs

Icons

Lucide, 16px in controls and menus, 20px in empty states — replaces flowbite solid icons
search
plus
list-filter
ellipsis
eye
pencil
trash
save
house
chevrons-up-down
moon
panel-left
car
dashboard
users
scroll-text
sparkles
cloud-upload
calendar
circle-check
triangle-alert
info
ban
log-out

Token map

Which existing light* tokens in modules/styles.ts each Hairline token feeds; dark* mirrors it
Hairline token
Existing tokens
bg
lightHtml · lightList · lightSidebar · lightNavbar · lightFormHeading
surface
lightListTable · lightListTableHeading · lightForm · lightInputBackground · lightDropdownOptionsBackground · lightDialogBackgorund · lightTableBackground
surface-2
lightSidebarItemHover · lightListTableRowHover · lightDropdownOptionsHoverBackground · lightSecondaryDarken · lightInputBackgroundHover
surface-3
lightSidebarItemActive · lightVerticalTabsBackgroundActive · lightButtonGroupActiveBackground · lightSkeletonBackgroundColor · lightProgressBarUnfilledColor
border
lightSidebarBorder · lightNavbarBorder · lightListBorder · lightFormBorder · lightTableBorder · lightDialogBreakLine · lightSidebarDevider
divider
lightShowTableBodyBorder (rows of the show, create and edit tables)
border-strong
lightInputBorder · lightDropownButtonsBorder · lightCheckboxBorderColor · lightButtonGroupBorder · lightDropzoneBorder
text
lightSidebarTextActive · lightFormFieldTextColor · lightInputText · lightDialogHeaderText · lightTextLabel · lightShowTableHeadingText · lightShowTableBodyText
text-2
lightSidebarText · lightListTableHeadingText · lightListTableText · lightTableText
muted
lightSidebarIcons · lightNavbarIcons · lightSidebarHeading · lightInputIconColor · lightBreadcrumbsHomepageText · lightListTablePaginationHelpText
faint
lightInputPlaceholderText · lightDropdownButtonsPlaceholderText · lightDatePickerPlaceHolder
ink / on-ink
lightButtonsBackground · lightButtonsBorder · lightButtonsText · lightTooltipBackground · lightTooltipText
accent
lightPrimary · lightCheckboxBgChecked · lightToggleBgActive · lightInputFocusBorder · lightProgressBarFilledColor · lightDatePickerCalendarDateActiveButtonBackground
ring
lightFocusRing · lightInputFocusRing · lightButtonFocusRing · lightToggleRing (accent at 26%)
danger
lightAcceptModalConfirmButtonBackground · lightInputErrorColor · lightRequiredIconColor
scrim
lightScrim

ScreensList view

Create
Identifier
Model
Price
Engine Type
Engine Power
Year
Listed
Mileage
Created At
Actions
3f9a21c4
$6,420
Electric
—
2023
Yes
18,400 km
Oct 14, 2026
b71e0d93
$8,950
Hybrid
218 hp
2024
Yes
6,120 km
Oct 14, 2026
9c04f5e2
$4,300
Gasoline
184 hp
2019
No
72,900 km
Oct 13, 2026
e2d8a710
$7,180
Gasoline
302 hp
2022
Yes
31,050 km
Oct 13, 2026
5a6b3c19
$9,640
Diesel
265 hp
2021
Yes
44,210 km
Oct 12, 2026
71fe2b08
$3,250
Diesel
150 hp
2017
No
118,700 km
Oct 12, 2026
c83a9d4f
$2,890
Gasoline
110 hp
2016
Yes
96,300 km
Oct 11, 2026
0b5e7a26
$7,760
Electric
—
2025
Yes
2,480 km
Oct 10, 2026
d4c1f803
$5,120
Hybrid
196 hp
2020
No
58,640 km
Oct 10, 2026
68a2e9b5
$6,050
Gasoline
240 hp
2023
Yes
21,900 km
Oct 9, 2026
aa93c07e
$8,300
Electric
—
2024
Yes
9,350 km
Oct 8, 2026
2e7f4b61
$4,780
Diesel
177 hp
2018
No
83,020 km
Oct 8, 2026

Showing 1–12 of 248 entries

ScreensCreate view

Cancel
Model *
Price *
Engine Type
Engine Power
Production Year
Description
Listed
Mileage
Color
Body Type
Promo Picture
Photos
Specifications

Detailed technical specification sheet

Trim * Trim / edition name
Segment
Doors 4
26
Condition
Exterior Color
Notes
Seller

ScreensShow view

Edit
General
Identifier
8f2c1a7e
Model
Aether Nimbus S
Price
7180.00 USD
Engine Type
Gasoline
Engine Power
184 HP
Production Year
2019
Created At
18 Oct 2026, 14:32
Listing
Description
Low-mileage hatchback with a full dealer service history. One owner, garage kept.
Listed
Yes
Body Type
Hatchback
Color
Graphite

ScreensUser menu

Header · af-settings-popup

User menu

Name and login on top, plugin items (language) and Settings as accordions, Log out alone at the bottom in red.

Click the avatar and rows in the first panel
Liveopen · accordions · pick language
Language openLanguageInUserMenu
Settings open · darkuserMenuSettingsPages

ComponentsActions

AFCL · /af-components

Actions

Ink for the one main action on a screen, hairline for everything else.

ButtonButtonGroupLinkLinkButtonDropdownMenu
<Button>variant · size · iconOnly · loader · disabled
Primaryvariant="primary"
Secondaryvariant="secondary"
Dangervariant="danger" · "danger-soft"
Loader:loader
Ghostvariant="ghost" · size="sm"
Icon onlyiconOnly · spinner replaces the icon
<ButtonGroup>segmented choices — one stays selected
Hairlinedefault
Solid colorsolidColor
Icon + label#icon slot
<Link>router links and external links
With iconexternal · next
<LinkButton>navigation that looks like a button
Primaryto="/create"
Secondaryvariant="secondary"
Ghostvariant="ghost"
Compactsize="sm"
Block & disabled:disabled
<DropdownMenu>trigger slot + DropdownMenuItem · closes on select, outside click and Esc
Items#icon · danger · disabled
Driftstone Nocturne3,911.78
Row actionsteleportToBody · placement="bottom-end"
AccordionDropdownMenuAccordion · v-model:open

ComponentsInputs

AFCL · /af-components

Inputs

34px controls, strong hairline at rest, accent border plus soft ring on focus, red ring on error.

InputTextareaSelectCheckboxToggleDatePickerDropzone
<Input>label · prefix/suffix · error · masked · disabled
ModelShown in list and record title
Defaultplaceholder
Search
Focus + icon#prefix
Price
Affixesprefix · suffix
Production Year *Must be an integer
Errorvalidation
API key
Masked · disabledunmasked · readonly
<Textarea>same frame as Input
Notes
Defaultrows="4"
Comment
Focusring
Reason *At least 10 characters
Errorvalidation
Generated summary
Disabledreadonly
<CodeInput>one field drawn as cells · autofill and paste fill the whole code
Focusautofocus · groupSize="3"
Typingv-model · @complete
Wrong or expired code
Errorinvalid · clear()
Disableddisabled
<Select>single · multiple · searchable · foreign resource
Engine Type
Body Type
Closedvalue · placeholder
Opensingle
Engine Type
Electric Hybrid Diesel
3 selected
Multiple:multiple="true"
SearchableforeignResource
Seller *This field is required
Region
Error · emptyrequired · no options
<Checkbox>16px, accent fill when on
Uncheckeddefault
Checkedv-model="true"
With description#default slot
Disabled:disabled
<Toggle>for settings that apply immediately
Listed
Offdefault
Listed
Onv-model="true"
Two-factor loginAsk for a code from the authenticator app
Setting row#default slot
Managed by admin
Not available
Disabled:disabled
<DatePicker>date · range · time — calendar in a popover
Created At
Listed On
Closedempty · value
October 2026
MoTuWeThFrSaSu
Inspection date Ink fill marks the picked day, hairline ring marks today.
Open calendarinteractive
Created between
RangeCustomRangePicker
Service due
Stored in UTC, shown in local time
Date + timetype="datetime"
<Dropzone>idle · drag-over · uploaded · rejected
Idledefault
Drop to upload 2 files
Drag overdragging
inspection.pdf412 KB · uploaded
Uploadedfile list
video.mov is 48 MB Max size is 10 MB · try another file
Rejectedsize / type

ComponentsData display

AFCL · /af-components

Data display

White surfaces on the gray ground, 1px hairlines instead of shadows, muted headers and tabular numbers.

TableCardVerticalTabsJsonViewerCountryFlagBadge
<Table>default · sortable · even highlights · empty · loading
JobStatusProgressStarted
Translate 1,240 stringsRunning
64%
2 min ago
Generate promo imagesDone
100%
1 h ago
Import cars.csvFailed
23%
Yesterday
1–3 of 3
Default:columns :data :pageSize
Model PriceYear
Aether Nimbus S$6,4202023
Borealis Aurora X$8,9502024
Cinder Kiln Coupe$4,3002019
Sortablecolumns[].sortable
ActionUserWhen
edit cars_sladmin14:20
create cars_pgadmin14:02
delete adminuserroot13:47
edit translationsadmin13:31
Even highlightsevenHighlights
ModelPriceYear
No cars match these filters Try removing “Premium” or clearing the model search.
Emptyno data
ModelPriceYear
Loading:isLoading
<Card>title · description · content

Storage adapter

Uploads are saved to the local filesystem under /db/uploads.

Plaintitle · description
<PageHeader>title · description · actions on the right, they wrap under the text on narrow screens
MCP

Connect an AI agent to dev-demo. It acts on your behalf with your permissions.

With action#actions
Connected accounts

Connect an external account to sign in without a password.

Text onlytitle · description
<VerticalTabs>tabs sit in a framed column, gray pill marks the active tab, same as the sidebar
Trim
Default#tab:general
With icons#tab slot
Settings
Signed in as adminforth
Active sessions

Every browser you signed in from has its own session.

In a card (settings page)tabsClass · contentClass · #tabs-header
<JsonViewer>laid out like Prettier, folds in the gutter, mono 12px, one accent for strings, copy on hover
{
"trim": "Base",
"photos": ["front.jpg", "side.jpg"],
"doors": 4,
"certified": true
}
Expandeda node within 60 chars stays on one line
"specifications": {"engine": "2.0 TDI", "power": 150, …} 7 keys
"photos": ["front.jpg", "side.jpg", …] 5 items
Foldedthe value in one line + its size
{"campaign": "cs2026", "channel": "linkedin", …}4 keys
Inlineinline · a click opens it in a popover
<CountryFlag>2px radius, hairline edge so white stripes don’t vanish
Sizessize="sm" 16 · size="md" 32 · class for 24, 48
Germany Italy
With labelcountry name
<Badge>20px pill, status tokens: *Soft fill, *Border edge, base text
Default Paid Pending Failed New Archived
Variantsdefault · success · warning · danger · accent · muted
Yes No
Boolean cellsuccess with check · muted

ComponentsOverlays & feedback

AFCL · /af-components

Overlays & feedback

The only layer with real shadow; 12px radius, gray footer band for actions, status always paired with an icon.

DialogModalTooltipToastProgressBarSpinnerSkeleton
<Dialog>confirm · destructive · form · sheet on phones
Confirm:buttons

Delete 2 cars?

Photos stored in S3 are removed too. This can’t be undone.

DestructiveAcceptModal
Form#default slot
No header, built-in Closesecondary
Phone sheetbelow sm, main action on top
<DialogPanel>a dialog on the page: login, two-factor, password reset · no backdrop, can’t be closed

Reset password

We will send a link to your email
Default#icon · title · subtitle · #footer

Set new password

Passwords do not match
Errorerror
<Modal>scrim + container — content is yours, also blocks the page while a task runs
CenteredcloseByClickOutside
Busyv-model:open
<Tooltip>ink, shown on hover
Remove selection
Inkdefault
<Toast>white card, colored icon only — text stays in ink
Car createdAether Nimbus S
Successvariant="success"
Dangervariant="danger"
3 rows skippedDuplicate identifiers
Warningvariant="warning"
Job startedTranslating 1,240 strings
View
Info + actionbuttons
Exporting 248 rows…Closes when the file is ready
Pendingtimeout bar
Stacknewest in front, 2 more peek 12px
Stack, hover8px gap, max 50vh, scrolls, timers paused
<ProgressBar>6px track, accent fill, values on the right
Default:currentValue
Translating793 / 1,240
Label + valuesshowValues
<Spinner>2px ring, accent head
Sizes12 · 16 · 24 · 32
Loading records…
Inlinewith text
<Skeleton>surface-3 blocks, soft pulse, mirrors the real layout
Texttype="text"
List itemavatar + lines
Imagetype="image"
FormCreateEditSkeleton

ComponentsCharts

AFCL · /af-components

Charts

Thin 2px lines, 4px rounded bar ends, recessive grid, one y-axis, legends with values, categorical slots in fixed order.

slot 1slot 2slot 3slot 4slot 5
<AreaChart>single · two series · stacked
Records created · 24h
1,762
00:0006:0012:0018:0023:00
Single + hovercrosshair tooltip
Created vs deleted · 14d
+577
Oct 1Oct 7Oct 14
Created 68Deleted 14
Two seriesoverlap
Listings by engine · 12 mo
117
NovFebMayAugOct
Gasoline 42Hybrid 38Electric 37
Stackedstacked: true
<BarChart>vertical · horizontal · stacked
Cars by body type
201
SedanSUVHatchCoupeWagonVan
Verticaldefault
Top models by price
$9,640
Eclipse Umbra RS$9,640
Borealis Aurora X$8,950
Lumina Prism$8,300
Horizon Bluehour EV$7,760
Driftstone Skylark R$7,180
Horizontalhorizontal: true
Production year mix
293
202120222023202420252026
GasolineHybridElectric
Stackedstacked: true
<PieChart>donut · pie
Engine type
Gasoline 96Diesel 58Electric 52Hybrid 42
Donuttype="donut"
Body type
SUV 25%Sedan 21%Hatch 15%Coupe 8%Other 31%
Pie + Othermax 4 slices
<MixedChart>bar + line on one shared axis — never two y-scales
New listings · 14d
188
Oct 1Oct 7Oct 14
Daily 197-day avg 15.4
Bars + averagecolumn + line
<TreeMapChart>area = count · 2px gaps · labels only where they fit
Cars by brand
Single huesequential ramp