Handoff: SummerCMS Admin Panel — Direction C ("Generic modern dashboard")
Overview
This is the admin panel for SummerCMS, a CMS framework. The mocks use data from one real app: Płytarium, a home catalogue of vinyl, CD and cassette releases. The panel is a schema-driven Vue 3 SPA. Every list and form is generated from YAML-defined columns and fields. Build generic components, not screens: one DataTable, one FormField row, one Tabs component, one RelationManager. The UI language is Polish, and all copy below is final.
Screens covered: Login, Navigation shell (multi-plugin: Płytarium + Użytkownicy), List (Albumy) with its selected/empty/loading states, Form (Edycja albumu) with tabs and 422 errors, and the relation manager (Kolekcje → Redaktorzy) with its "Dodaj redaktora" modal. Light and dark mode are both included, plus tablet width.
About the Design Files
Direction C v2.dc.html is a design reference built in HTML, not production code. Recreate it in the target Vue 3 codebase using its existing patterns. If the codebase has no UI setup yet, use Vue 3 + Vite + Tailwind with headless primitives (e.g. Radix Vue / Reka UI) and lucide-vue-next icons.
To view it, open Direction C v2.dc.html in a browser. support.js must be in the same folder, and you need internet access for the fonts and icons. The component's props act as a state switcher:
plugin:plytarium | users(useuserswithscreen=listto see the Users plugin list)flyout: boolean; withnarrow, shows the collapsed-rail flyoutscreen:login | list | form | relationlistState:default | selected | empty | loadingtab:basic | detailsdark,narrow(tablet),errors(422),toast,modal,userMenu: booleans
All sample data sits in the ALBUMS, EDITORS and CANDS constants in the logic class.
Fidelity
High-fidelity. Colours, type, spacing, radii and copy are final. Match them closely.
Design Tokens
Colours
| Token | Light | Dark | Use |
|---|---|---|---|
| bg | #f4f6f9 |
#111726 |
App background behind cards |
| surface | #ffffff |
#182033 |
Cards, header, inputs, footer bar |
| subtle | #f3f5f8 |
#1f283d |
Table header, search input fill, chips, avatar bg |
| border | #e6e9ef |
#29334b |
Card borders, row dividers |
| borderStrong | #d2d8e2 |
#3a4661 |
Input borders, unchecked checkbox, switch off |
| text | #141b2d |
#eef1f6 |
Primary text |
| muted | #566175 |
#a9b3c6 |
Secondary text, column headers, icons |
| placeholder | #6b7588 |
#8a95ab |
Input placeholder |
| primary | #22304d (navy) |
#fcd34d (yellow) |
Primary buttons, checked checkbox |
| onPrimary | #ffffff |
#1b2540 |
Text on primary |
| danger | #c62828 |
#f58a8a |
Destructive, errors |
| dangerSoft | #fdf0f0 |
#3a1d24 |
Error banner bg, destructive hover |
| okBg / okText | #e3f4e8 / #1c6b35 |
#173826 / #8fdfa8 |
"Tak" badge |
| ring | rgba(252,196,40,.55) |
rgba(252,211,77,.45) |
Focus ring (3px) |
| hover | #f1f3f7 |
#212b42 |
Hover bg for ghost buttons/rows |
| sel | #fdf3cf |
#3a3622 |
Selected row bg, current page button |
| skel | #eceff4 |
#263049 |
Skeleton bars |
| overlay | rgba(20,27,45,.5) |
rgba(5,8,16,.7) |
Modal backdrop |
| side | #1d2740 |
#0d1320 |
Sidebar bg (sidebar is dark in both modes; dark mode adds a #222b40 right border) |
| accent | #fcd34d |
same | Brand yellow: logo, active nav, avatar |
| onAccent | #1b2540 |
same | Text on accent |
| sidebar text | #c3cbda idle, #9aa6bd section label, #ffffff hover, #fcd34d active |
||
| card shadow | 0 1px 2px rgba(20,27,45,.04), 0 4px 16px rgba(20,27,45,.05) |
none |
The brand is dark navy + sunny yellow. In light mode the primary is navy and yellow is only an accent. In dark mode yellow becomes the primary.
Typography
- UI font: DM Sans (Google Fonts, opsz 9..40, weights 400/500/600/700). Base size 14px, line-height 1.5.
- Mono: DM Mono 400/500. Used for the Półka shelf code and the
colorpickertoken. - Datetimes use DM Sans with
font-variant-numeric: tabular-nums, 13px, muted colour.
| Role | Size / weight / tracking |
|---|---|
| Page title (list) | 26px / 700 / -0.02em |
| Record title (form) | 24px / 700 / -0.02em |
| Login title | 22px / 700 / -0.02em |
| Modal title | 20px / 700 / -0.01em |
| Section title (Redaktorzy) | 17px / 700 |
| Body / cell | 14px / 400 (Nazwa cell 600) |
| Field label | 14px / 600 |
| Column header | 12px / 600, muted (the sorted column uses the text colour) |
| Helper / meta / error text | 13px |
Radii
Inputs, buttons and selects: 10px. Cards: 16px. Login card: 16px. Modal: 20px. Inner table container and toggle cards: 12px. Tabs track: 12px, tab pill: 9px. Checkbox: 5px (6px in the form and modal). Pagination buttons: 8px. Badges, chips and switch: 999px. Avatars: 50%.
Spacing
Page padding is 28px top/bottom and 32px left/right. Gaps between cards and blocks are 20px. Form card padding is 28px. The form grid gap is 22px (rows) × 24px (columns). Table cells use 14px horizontal padding, with 20px on the first and last columns.
Control heights
- Primary/secondary buttons and search: 42px
- Form inputs, selects and login inputs: 44px
- Table header row: 44px. Body row: 54px (56px in the relation manager). Modal option row: 54px.
- Pagination buttons: 34px
- Top header: 64px. Sidebar nav item: 40px.
Focus
Every interactive element shows a visible 3px yellow ring (box-shadow: 0 0 0 3px var(--ring)). Inputs also switch their border to primary. Buttons use outline: 3px solid var(--ring); outline-offset: 2px. Style :focus-visible this way and never remove it.
Screens
1. Login (screen=login)
- Full-viewport background:
radial-gradient(ellipse at 50% 35%, #2b3a5c 0%, #1d2740 60%). - A centred column, max-width 400px, gap 24px:
- Wordmark: sun icon (lucide
sun, 20px, yellow) in a 34px circle filledrgba(252,211,77,.14), followed by "SummerCMS" at 18px/700 in white, with "CMS" in yellow. This is the only branding, because the admin URL is custom (e.g./plytadmin). - Card: white background, radius 16, padding 32, gap 18, shadow
0 24px 60px rgba(0,0,0,.35).- Title: "Witaj ponownie"
- Field "Login lub e-mail" (input 44px)
- Field "Hasło" (password)
- Error state: the password input gets a danger border and
aria-invalid. Below it sits an alert block (role="alert", dangerSoft bg, danger text, radius 10, padding 12×14,circle-alerticon 18px) reading "Nieprawidłowe dane logowania". - Full-width primary button "Zaloguj" (44px).
- Wordmark: sun icon (lucide
2. Navigation shell — multi-plugin (rail + section panel)
SummerCMS replaces WinterCMS. It is a backend that hosts many plugins, and each plugin registers navigation the way Winter's registerNavigation() does: one main-menu item (label, icon, order, permissions) plus its own side menu (items, icons, permissions). Płytarium is just one of these plugins. The shell is built from that registry, and nothing in it is hardcoded per plugin.
Plugin rail (level 1 = Winter main menu)
- Always visible. Width 80px, colour
side(#1d2740light /#0d1320dark), padding 14px 0 12px, column gap 6, centred. - Top: brand badge, a 40px rounded square (radius 12) filled
rgba(252,211,77,.14)holding asunicon (22px, yellow).title="SummerCMS", bottom margin 14. - One item per registered main-menu entry, sorted by
order:- 68px wide, padding 8/6, radius 12, letter-spacing -0.01em.
- 20px icon, with the label below it at 11px (line-height 1.2, centred).
- Idle:
#c3cbda. Hover:rgba(255,255,255,.08)with white text. - Active: bg
rgba(252,211,77,.14), text/icon#fcd34d, weight 700,aria-current="page". - Focus: 3px ring.
- Mock entries: Płytarium (
disc-3), Użytkownicy (users), Media (image).
- A flex spacer, then Ustawienia (
settings) pinned to the bottom. Settings is a separate area where plugins register settings pages, the same as Winter. - When the panel is collapsed, a 40px "Rozwiń menu" button (
panel-left-open) appears above Ustawienia. - Wrap the rail in
<nav aria-label="Wtyczki">.
Section panel (level 2 = the active plugin's side menu)
- Width 224px,
surfacebg, 1pxborderon the right, padding 14×12, gap 2. - Header: 40px tall, 14px bottom margin. It shows the plugin label at 16px/700 (e.g. "Płytarium") and a 30px "Zwiń menu" button (
panel-left-close). - Items: 40px tall, radius 10, 18px icon, gap 12.
- Idle:
muted, weight 500. Hover:hoverbg withtextcolour. - Active:
selbg (#fdf3cf/#3a3622),textcolour, weight 600,aria-current="page".
- Idle:
- Płytarium items: Albumy (
disc-3), Artyści (mic-vocal), Kolekcje (library), Gatunki (tags).- "Style" is absent because the current admin lacks permission for it.
- Użytkownicy items: Użytkownicy (
users), Grupy (users-round), Role (shield-check).
Switching plugins
- Clicking a rail item navigates to that plugin's first permitted side-menu item (or to its registered default URL).
- The panel content changes, and the first breadcrumb changes to the plugin label ("Płytarium › Albumy", "Użytkownicy › Użytkownicy").
- The active rail item is derived from the current route's owning plugin, e.g.
/plytadmin/plytarium/albums→plytarium.
Permissions
- Filter both levels. An item the user can't access is not rendered at all; don't show it disabled.
- A plugin whose side menu ends up empty after filtering is not shown in the rail either.
Collapsed / tablet
- Below about 1100px, or when the user collapses it manually, the panel is hidden and only the rail remains. The manual choice persists in localStorage.
- In that state, hovering or focusing a rail item, or pressing Enter/↓ on it, opens a flyout with the plugin's side menu:
- Position: 8px right of the rail, aligned with the header area.
- Size and shape: 230px wide,
surfacebg, border, radius 14, padding 8. - Shadow:
0 16px 40px rgba(20,27,45,.18). - Contents: a bold plugin title, then items 38px tall with radius 9 and the same active and idle styling as the panel.
- It uses
role="menu", closes on Esc or mouse-leave with a ~200ms delay, and returns focus to the rail item.
Suggested registry shape (what the backend exposes to the SPA):
[{ "code": "plytarium", "label": "Płytarium", "icon": "disc-3", "order": 100,
"url": "/plytarium/albums",
"sideMenu": [{ "code": "albums", "label": "Albumy", "icon": "disc-3", "url": "/plytarium/albums" }, …] }]
The server filters this by permissions before sending it.
Tip: when there are many plugins, add a Ctrl K command palette that searches every plugin's side-menu entries.
Top header
- 64px tall, surface bg, bottom border, padding
0 24px 0 32px. - Left: breadcrumbs "Płytarium › Albumy › Enigmatic". Parent crumbs are muted links; the current crumb uses the text colour at weight 600. Separator:
chevron-right14px. - Right: user button, 44px tall, radius 12, containing:
- 34px yellow avatar with initials "TW"
- Name "Tomasz Wójcik" (600) above role "Administrator" (12px muted). The name and role hide on tablet.
chevron-down
- User menu (dropdown,
role="menu"): 280px wide, radius 14, padding 8, shadow0 16px 40px rgba(20,27,45,.16). It holds a header (40px avatar, "Tomasz Wójcik" in bold, "tomasz.wojcik@plytarium.pl" in muted), a divider, and the menu item "Wyloguj" (log-outicon).
3. List — Albumy (screen=list)
Heading row
- "Albumy" (26/700) with the subtitle "348 pozycji w katalogu".
- Primary button "Nowy album" (
plusicon), aligned right.
Card (radius 16, border, shadow), containing a toolbar, a table and a pagination footer.
Toolbar (padding 16×20)
- Search input, 320px wide,
subtlefill,searchicon at left 14px. Placeholder: "Szukaj albumów…". - Right side:
- No rows selected: "Usuń zaznaczone" shows as a disabled outline button (opacity .6,
not-allowed, nativedisabled). - 1+ rows selected: a pill "Zaznaczono 3" (sel bg,
checkicon) appears next to an enabled danger-outline button "Usuń zaznaczone" (trash-2, hover dangerSoft).
- No rows selected: "Usuń zaznaczone" shows as a disabled outline button (opacity .6,
Table — min-width 920px; scrolls horizontally inside the card on narrow screens. The header is sticky with subtle bg and borders top and bottom. Columns in order:
- Checkbox (52px). The header checkbox is tri-state: empty,
mixed(minus icon) or checked. - Nazwa: sortable, currently ascending. The header shows
arrow-up14px in the text colour witharia-sort="ascending". Cells are weight 600 and link to the edit form. - Artyści: relation column, comma-joined names, muted.
- Format (110px): a pill (26px tall, subtle bg) with an icon. Winyl uses
disc-3, CD usesdisc, Kaseta usescassette-tape. - Gatunek: shows "—" (muted) when empty.
- Półka (72px): DM Mono 13px.
- Utworzono (150px):
YYYY-MM-DD HH:mm, muted, 13px, tabular numbers. - Wypożyczony (128px): switch column rendered as a badge. "Tak" is a green pill with an
arrow-up-righticon; "Nie" is a muted outline pill.
- Row: 54px tall with a bottom border. Selected rows use the
selbg. - Column headers are 12px/600 and muted, except the sorted column, which uses the text colour.
Footer (padding 14×20, top border, 13px muted)
- "1–12 z 348"
- "Na stronę" followed by a select button showing "12"
- Right-aligned pager:
- "Poprzednia" (disabled on page 1)
- Page buttons 1, 2, 3, …, 29. The current page has
selbg, weight 700 andaria-current. - "Następna"
States
- Selected: in the mock, rows 3, 6 and 7 are selected (Enigmatic, Nevermind, Nowe sytuacje).
- Empty:
- 80px vertical padding inside the table area.
- 56px circle containing a
search-xicon. - Title "Nic nie znaleziono" (17/700).
- Text: "Żaden album nie pasuje do frazy „kaseta 1983”. Spróbuj innej nazwy lub artysty."
- Outline button "Wyczyść wyszukiwanie".
- The footer shows "0 wyników" with no pager.
- Loading:
- 8 skeleton rows at the same row height.
- Bars are 12px tall with radius 6. Pill placeholders match the badge sizes. Bar widths vary per row.
- The footer text reads "Wczytywanie…".
Sample rows (sorted by Nazwa). Columns: Nazwa · Artyści · Format · Gatunek · Półka · Utworzono · Wypożyczony.
| Nazwa | Artyści | Format | Gatunek | Półka | Utworzono | Wypożyczony |
|---|---|---|---|---|---|---|
| Astigmatic | Krzysztof Komeda | Winyl | Jazz | J-02 | 2026-09-21 14:32 | Nie |
| Ella and Louis | Ella Fitzgerald, Louis Armstrong | Winyl | Jazz | J-04 | 2026-09-20 19:05 | Tak |
| Enigmatic | Czesław Niemen, Akwarium | Winyl | Rock progresywny | R-11 | 2026-09-18 10:47 | Nie |
| Kind of Blue | Miles Davis | Winyl | Jazz | J-01 | 2026-09-17 21:13 | Nie |
| Legenda | Armia | Kaseta | Punk | K-03 | 2026-09-15 08:26 | Nie |
| Nevermind | Nirvana | CD | Grunge | C-07 | 2026-09-14 16:58 | Tak |
| Nowe sytuacje | Republika | Kaseta | Nowa fala | K-01 | 2026-09-12 12:09 | Nie |
| OK Computer | Radiohead | CD | Rock alternatywny | C-12 | 2026-09-10 18:40 | Nie |
| Rumours | Fleetwood Mac | Winyl | — | R-04 | 2026-09-08 20:15 | Tak |
| Spokojnie | Kult | Winyl | Rock | R-02 | 2026-09-05 11:31 | Nie |
| Tutu | Miles Davis | CD | Jazz fusion | C-03 | 2026-09-03 09:52 | Nie |
| Unknown Pleasures | Joy Division | Winyl | Post-punk | R-09 | 2026-09-01 22:04 | Nie |
4. Form — Edycja albumu (screen=form)
Content column max-width 980px, gap 20.
Header row
- 40px back button (
arrow-left, radius 10, border) labelled "Wróć do listy". - Title "Enigmatic" (24/700) with the subtitle "Edycja albumu · Czesław Niemen, Akwarium".
- Tabs on the right, styled as a segmented control:
- Track: subtle bg, 1px border, radius 12, padding 4.
- Tab: 34px tall, radius 9, 600 weight.
- Active tab: surface bg with shadow
0 1px 3px rgba(20,27,45,.12). - Tabs: "Podstawowe" and "Szczegóły", with
role="tablist"/"tab"andaria-selected.
422 error banner (errors state only): dangerSoft bg, radius 12, padding 14×18, circle-alert icon 20px. Text: "Nie udało się zapisać. Popraw 2 pola oznaczone poniżej."
Form card: radius 16, padding 28.
- Grid:
repeat(auto-fit, minmax(280px, 1fr)), gap 22×24. - In the real implementation, map the YAML
spanvalue to columns:left→ column 1,right→ column 2,full→grid-column: 1 / -1. Below about 600px both columns collapse to one. - FormField row: label (600, with a red
*for required fields that isaria-hidden; mark the inputrequired/aria-required), then the control, then the error message.- Error message: 13px, danger colour,
circle-alert14px icon, linked througharia-describedby. - The input border turns danger and the input gets
aria-invalid="true".
- Error message: 13px, danger colour,
Podstawowe tab fields
| Field | Type | Span | Notes |
|---|---|---|---|
| Nazwa * | text | left | Error: "Pole Nazwa jest wymagane." (value empty) |
| Artyści * | multi-select relation with search | right | Chips: 30px pill, subtle bg, 22px primary-colour initials avatar, name (600), and a remove button labelled "Usuń: {name}". An inline input "Dodaj artystę…" opens a searchable dropdown. |
| Format * | dropdown | left | Options: Winyl / CD / Kaseta. The selected value shows its format icon. |
| Gatunek | relation dropdown | right | First option is "— brak —", shown muted when selected. |
| Półka | text | left | Error: "Pole Półka może mieć maksymalnie 12 znaków." (value "Regał w salonie, góra") |
| Opis | textarea, 4 rows | full | Resizes vertically. |
| Wypożyczony | switch | left | Rendered as a toggle card: border, radius 12, padding 14×16. Title "Wypożyczony", helper text "Płyta jest aktualnie poza domem", and a 44×26 switch (role="switch", off state uses the borderStrong track). |
| Ulubiony | checkbox | right | Toggle card with helper text "Pokazuj na początku listy" and a 20px checkbox, checked in the mock. |
Szczegóły tab fields
- Rok wydania (1970)
- Wytwórnia (Polskie Nagrania Muza)
- Numer katalogowy (XL 0569)
- Stan nośnika (select: "Bardzo dobry (VG+)")
- Kolor okładki, which uses the unknown field type fallback:
- 1.5px dashed borderStrong border, subtle bg, radius 10, min-height 44.
puzzleicon with the text "Nieobsługiwany typ pola:colorpicker" (13px muted; the type name in DM Mono, text colour).- The FieldRenderer should fall back to this for any unregistered
type.
(These Szczegóły fields are placeholder content; the real ones come from the YAML.)
Sticky footer: surface bg, top border, padding 14×32, always visible below the scrolling form.
- Left: "Usuń", a danger outline button with
trash-2. - Right, in order:
- "Anuluj" (ghost, muted)
- "Zapisz i zamknij" (outline)
- "Zapisz" (primary)
Toast after save
- Bottom-centre, 88px from the bottom, so it clears the footer.
- Navy
#1d2740bg, white text, radius 14, shadow0 16px 40px rgba(0,0,0,.25). - 26px yellow circle with a check icon.
- Text: "Album „Enigmatic” został zapisany".
- Close button with
aria-label="Zamknij". - Use
role="status"and auto-dismiss after about 4s.
5. Relation manager — Kolekcje → Redaktorzy (screen=relation)
- The record header shows "Kolekcja domowa" with the subtitle "Edycja kolekcji". Tabs: "Podstawowe" and "Redaktorzy" (active).
- The "Redaktorzy" tab (span full) renders only when editing an existing record, never on create.
Section header
- "Redaktorzy" (17/700) with the helper text "Osoby, które mogą edytować tę kolekcję".
- On the right:
- Search, 220px wide, 38px tall, placeholder "Szukaj…"
- "Usuń zaznaczonych" (danger outline,
user-minus) - "Dodaj redaktora" (primary,
user-plus) - These buttons are 38px tall, one size down from page level.
Table: the same DataTable, inside a bordered 12px-radius container.
- Columns: checkbox, Imię i nazwisko (30px initials avatar plus name, 600), E-mail (muted), Dodano (160px, datetime).
- Rows are 56px tall. Row selection works the same as the main list. In the mock, Marek Nowak is selected and the header checkbox is
mixed.
Sample editors:
- Anna Kowalska · anna.kowalska@plytarium.pl · 2026-08-02 17:20
- Marek Nowak · marek.nowak@plytarium.pl · 2026-08-14 09:03
- Julia Wiśniewska · julia.wisniewska@plytarium.pl · 2026-09-19 20:41
"Dodaj redaktora" modal
- Container:
role="dialog",aria-modal, 560px wide, radius 20, padding 24, gap 16, over the backdrop overlay. - Title "Dodaj redaktora" (20/700). Helper text: "Wybierz osoby spoza obecnych redaktorów. Właściciel kolekcji nie jest wyświetlany."
- 34px close button (
x, subtle bg). - Search input (44px, autofocused, so the focus ring shows), placeholder "Szukaj po nazwisku lub e-mailu…".
- Candidate list:
role="listbox",aria-multiselectable.- Each option row: 54px tall, radius 12, border, 32px initials avatar, name (600) above email (13px muted), and a checkbox on the right.
- Selected option: sel bg and a
#e0b020border.
- Candidates are users not yet linked to the collection, with the owner excluded on the server side. Results are paginated at 5 per page: "1–5 z 14", with prev/next buttons (34px, radius 9).
- Footer: two equal-width buttons, "Anuluj" (outline) and "Dodaj (N)" (primary). N is the selection count; the button is disabled when N = 0.
- Mock: Piotr Zieliński and Katarzyna Lewandowska are selected, so the button reads "Dodaj (2)". Other candidates: Michał Kamiński, Agnieszka Dąbrowska, Paweł Szymański.
- Keyboard: Esc closes, focus is trapped inside, and focus returns to "Dodaj redaktora" on close.
Interactions & Behavior
- Sorting: clicking a sortable header cycles asc → desc → none. Show
arrow-up/arrow-down, and reflect the state inaria-sortand the URL query. - Search: debounce at 300ms, reset to page 1, and keep the term in the URL.
- Selection:
- The header checkbox selects the current page.
- Selection resets on page or search change.
- Bulk delete opens a confirm dialog: "Usunąć N albumów?".
- Pagination: per-page options 12 / 24 / 48 / 96. Range text uses the format "{from}–{to} z {total}" with an en dash.
- Loading: show skeleton rows in place of the table body. Keep the toolbar and footer mounted.
- Validation:
- On a 422
{errors: {field: [msg]}}response, show the messages under each field plus the top banner, and focus the first invalid field. - Errors clear per field on change.
- When the invalid field sits on another tab, mark that tab with a count badge.
- On a 422
- Save:
- "Zapisz" stays on the form and shows the toast.
- "Zapisz i zamknij" returns to the list and shows the toast there.
- "Anuluj" returns without saving; confirm first if the form is dirty.
- "Usuń" opens a confirm dialog.
- Relation manager: "Dodaj redaktora" opens the modal, and confirming POSTs the selected IDs. The table then refreshes and a toast reads "Dodano 2 redaktorów". "Usuń zaznaczonych" asks for confirmation, then detaches.
- Transitions: use subtle ones only, 150ms ease-out for hover/background and 200ms for modal fade and scale (0.98 → 1). No decorative animation.
- Responsive:
- 1280px is the design width.
- Below about 1100px the sidebar collapses to 72px icons; users can also toggle this manually and the choice persists.
- At tablet width (≥768px) tables scroll horizontally inside their card and the form grid wraps to one column when needed. The header user button hides its name.
State Management
- Global: auth user and permissions (these drive which menu items render), plus the sidebar collapsed flag.
- Per list:
{ search, sort: {column, dir}, page, perPage, selectedIds, loading, rows, total }, synced to the URL query. - Per form:
{ record, dirty, errors: Record<field, string[]>, activeTab, saving, mode: 'create'|'update' }. Tabs and fields come from the YAML schema; relation tabs requiremode === 'update'. - Relation modal:
{ open, search, page, candidates, total, selectedIds }. - Toast queue: global.
Components to build (generic)
PluginRail,SectionPanel,SectionFlyout(all driven by the navigation registry)AppShell(sidebar + header)DataTable(columns from schema; column typestext,relation,datetime,switch,badge/format; selection; sort; states)ListToolbarPaginationFormTabsFormGridFormField(label/control/error row;span)FieldRenderer(type registry →text,textarea,dropdown,relation,relation-multi,switch,checkbox, fallbackUnsupportedField)RelationManagerRelationPickerModalToastUserMenu
Assets
- Icons: lucide (the mock uses the
lucide-staticicon font). Uselucide-vue-nextin production. Icons used:- Navigation and brand:
sun,disc-3,disc,cassette-tape,mic-vocal,library,tags,settings,panel-left-close - Chevrons and arrows:
chevron-right,chevron-down,chevron-left,arrow-up,arrow-left,arrow-right,arrow-up-right - Actions and status:
search,search-x,plus,trash-2,check,minus,x,circle-alert,user-plus,user-minus,log-out,puzzle
- Navigation and brand:
- Fonts: DM Sans and DM Mono from Google Fonts.
- No images. The logo is the lucide
sunicon plus the text wordmark.
Files
Direction C v2.dc.html: the full design reference, with every screen and state switchable via props.support.js: runtime needed to open the HTML file locally. It is not part of the design.