7.2 KiB
This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ before writing any code. Heed deprecation notices.
Astryx v0.2.0 · 154 components
CLI: run every command as npx astryx <cmd> (shown below as astryx ...).
SETUP (once, in your app entry e.g. main.tsx) — without these, components render unstyled: import "@astryxdesign/core/reset.css"; import "@astryxdesign/core/astryx.css";
WORKFLOW — discover, don't guess. Before writing UI:
astryx build "<idea>"— START HERE: returns a kit (closest [page] + [block]s + [component]s). No args = full playbook.astryx template <name> [--skeleton]— scaffold the [page]/[block]s it named, or study their layout. Templates are reference code.astryx component <Name>— props + examples for every component you use.
RULES:
- No — components do all layout/spacing. Full page → AppShell; sidebar nav → SideNav.
- Frame first: pick the shell (AppShell / Layout+LayoutPanel) and budget regions in px BEFORE writing content (
astryx docs layout). - Dense data = rows (Table, List/Item) edge-to-edge — never Card-wrapped list items. Card = dashboard widgets, galleries, settings groups only.
- Status → StatusDot/Token; Badge only for counts and enumerated states, never decoration.
- Custom styling: component props first; else Tailwind utilities backed by tokens (bg-surface, text-primary, rounded-lg) via tailwind-theme.css. No raw hex/px.
- Tokens for every value (
astryx docs tokens). Brand/accent viaastryx theme— never override --color-* in :root. - SELF-CHECK before you finish: re-read the file and replace any style={{…}}, raw / layout, imported .css/@apply, or hardcoded/arbitrary value (e.g. bg-[#fff], p-[13px]) with the component or a token-backed utility. If unsure a component/prop exists, run
astryx component <Name>/astryx search "<thing>"; don't hand-roll CSS.
MORE CLI: search "" find any component / hook / doc / template / block component --list 154 components by category template --list page + block recipes docs color, elevation, icons, illustrations, internationalization, layout, migration, motion, principles, shape, spacing, styling, theme, tokens, typography swizzle eject component source for deep customization upgrade --apply run after any @astryxdesign/core bump
Loyaly Merchant Dashboard — project rules
Toolchain
- Node 22 is required (
.nvmrc→ 22.23.1). The astryx CLI hard-fails on Node 20.nvm usebefore running anynpx astryxcommand ornpm run theme:build. - Dev server runs on port 3100 (
lytsup-siteowns 3000).
Theme
- All design values live in
src/theme/loyalyTheme.ts. Edit that, then runnpm run theme:build, then commit the generatedsrc/theme/loyaly.{css,js,d.ts,variants.d.ts}. theme:buildis deliberately NOT wired toprebuild— it would breaknpm run buildon Node 20.- Import the theme from
@/theme, never@/theme/loyalydirectly.@/themere-attaches the Lucide icon registry, whichastryx theme buildcannot serialise (icons are React components). Skipping it silently degrades every<Icon>to Astryx's default glyph set. globals.cssimports../theme/loyaly.css, NOT@astryxdesign/theme-neutral/theme.css. Astryx theme CSS is@scope-gated on the theme name, so neutral's rules match nothing under[data-astryx-theme="loyaly"].
The monochrome rule
Gray is the accent. The only colour permitted is semantic: success, warning, error.
- Astryx's categorical hues (blue/cyan/teal/purple/pink/orange) are aliased to gray in
aliasHueToGray(). green/red/yellow deliberately keep hue — Banner's semantic statuses resolve through those hue tokens. - Some neutral-theme component styles hardcode colour instead of reading a token
(
badge variant:info,progressbar variant:accent). Those need an explicitcomponentsoverride;aliasHueToGray()cannot reach them. /tokens(dev-only route) is the audit page. After any theme change, load it and run the chromatic sweep in the browser console — it should report 0 non-semantic colour nodes.
Known upstream issues (Astryx 0.2.0)
useEntryAnimationbreaks SSR hydration. Its source assumes'use client'modules never run on the server; in the App Router they do. AnyFieldStatus(i.e.TextInput/TextAreastatus={...}) present at initial paint renders without the slide-down class on the server and with it on the client → an unpatchable class mismatch. Setting status on blur/submit (the normal path) is unaffected. Don't server-render a field that already has a status.- Component keys in
defineTheme({components})are inconsistently cased:text-inputbutprogressbar. The CLI warns on unknown keys, but that warning is not reliable in either direction:- A misspelled key (
textinput) is dropped silently — the CSS is never emitted. - A key the CLI doesn't know but Astryx does use (
table-cell,table-header-cell) warns yet still emits correctly. So never trust the warning alone. After anycomponentschange, grep the generatedsrc/theme/loyaly.cssfor the rule, then confirm the computed style in the browser. Valid targets are whateverthemeProps('...')is called with indist/<Component>/*.js.
- A misspelled key (
StyleOverridessupports structural pseudo-classes, not just interaction ones —':first-child'emits correctly (used for the table first-column lead-in).
Conventions
- Follow the Astryx rules above: no raw
<div>for layout, no hardcoded hex/px, component props first then token-backed Tailwind utilities. src/lib/icons.tsis the single icon map. Only the 26 semantic names resolve via<Icon icon="search"/>; everything else is<Icon icon={ICONS.stores}/>. Never import a lucide icon directly into a feature file.
Settings spacing contract
Every Settings screen renders inside src/features/settings/SettingsPage.tsx. Do not add
page padding in an individual settings page — change the container instead.
| value | why | |
|---|---|---|
| Horizontal | 32px (paddingInline={8}) |
clears the sub-nav and the Copilot rail |
| Top | 32px (paddingBlock={8}) |
title never touches the header |
| Bottom | 48px (className="pb-12") |
last card is never flush to the fold |
| Header → body | 32px (gap={8}) |
title block reads as its own layer |
| Between body blocks | 24px (gap={6}) |
cards, tables, toolbars |
SpacingStep stops at 10 (40px) and Stack has no paddingBlockEnd, which is why the 48px
bottom goes through the Tailwind bridge — pb-12 still resolves to --spacing-12, so no
raw pixel value enters the codebase. It sits in the utilities layer, which the cascade
puts after astryx-base, so it wins over paddingBlock without !important.
Card padding (24px) and table cell padding (12px, 20px first-column lead-in) are set once
in loyalyTheme.ts so every module agrees — not per page.