--- name: skillshare-ui-website-style description: >- Skillshare frontend design system for the React dashboard (ui/) and Docusaurus website (website/). Use this skill whenever you: build or modify a dashboard page or component in ui/src/, style or layout website pages or custom CSS in website/, create new React components for the dashboard, add pages to the dashboard, fix visual bugs in either frontend, or need to know which design tokens, ss-* classes, components, or patterns to use. Covers the two dashboard styles (Clean / Playful) in light and dark, design tokens, the ss-* class system, component API, page structure, accessibility, keyboard shortcuts, and anti-patterns. Even if the user just says "fix the styling" or "add a card", use this skill to ensure consistency. metadata: targets: [claude, universal] --- Enforce the skillshare design system across the two frontends. $ARGUMENTS is the file or area being worked on. Before acting, run `python3 scripts/ai-context.py frontend`. That topic and the CSS/source files it identifies are the source of truth; this skill retains the component catalog and visual QA checklist. | Aspect | UI Dashboard (`ui/`) | Website (`website/`) | |--------|---------------------|----------------------| | Stack | React 19 + Vite + Tailwind CSS v4 | Docusaurus 3 + custom CSS | | Source of truth | `ui/src/components.css` (tokens + `ss-*` classes), `ui/src/index.css` (Tailwind mapping) | `website/src/css/custom.css` (docs), `website/src/pages/*.module.css` (homepage, features) | | Looks | Two styles, **Clean** and **Playful**, each in light and dark | Docs: clean. Homepage: hand-drawn string board | **This file names tokens and classes and says when to use them. It does not copy their values.** Colours, radii, fonts and shadows change; read them from the CSS when you need one. --- ## UI Dashboard (`ui/`) Reference pages: `ui/src/pages/TargetsPage.tsx` (list page), `ui/src/pages/HubPage.tsx` (tabs), `ui/src/pages/ResourcesPage.tsx` (list + tiles + bulk toolbar). > Design rules and the reasoning behind them: `references/STYLE_GUIDE.md`. ### Two styles, two modes Style and mode are independent, so every screen has four looks. | Axis | Values | How it is set | |------|--------|---------------| | Style | Clean, Playful (default) | `html[data-theme="playful"]`; attribute absent = Clean | | Mode | light, dark, system | `html.dark` | Set by `ui/src/context/ThemeContext.tsx`, switched in `ThemePopover.tsx`. `?theme=clean|playful|dark|light` in the URL forces one, which is handy for screenshots. All four looks come from CSS variables alone. A page that uses only tokens and `ss-*` classes gets all four for free; a hardcoded colour, radius or shadow breaks three of them. - Clean: system font, 1px hairlines, soft shadows, ink-coloured primary button. - Playful: Kalam headings, 2px ink borders, hard offset shadows, dashed separators, yellow primary, pastel accents, dot-grid background, sticky-note tiles. - Style-only markup: `.ss-only-clean` / `.ss-only-playful` (Dashboard shows a count strip in Clean and a pin board in Playful). ### Design tokens Defined per look at the top of `ui/src/components.css`. `ui/src/index.css` exposes them to Tailwind through `@theme inline`, so `text-ink-2`, `bg-surface`, `border-line` all follow the active look. | Group | Tokens | Use | |-------|--------|-----| | Surfaces | `--bg` `--side` `--surface` `--sunken` | Page, sidebar, cards and inputs, recessed headers and footers | | Text | `--ink` `--ink-2` `--ink-3` | Primary, secondary, tertiary and placeholder | | Lines | `--line` `--line-2` `--line-soft` | Frames, control borders, soft dividers | | Borders | `--sep` `--frame` `--bw` | Whole `border` values: row separator, box frame, control border width | | Action | `--pri` `--on-pri` `--accent` `--accent-bg` `--sel` `--sel-ink` | Primary button, links and focus, selected nav and menu items | | Status | `--ok` `--warn` `--bad`, each with `-bg` | Text or dot colour, plus its tinted background | | Kind | `--c-skill` `--c-agent` `--c-extra` `--c-mcp` `--c-plugin` `--c-target`, each with `-bg` | Resource-kind colour, used by `.ss-cat` | | Pastels | `--pa` `--pb` `--pc` `--pd` `--pe` | **Playful only.** Never reference outside a `[data-theme="playful"]` rule | | Type | `--f` `--fh` `--fm` `--h1` `--h2` | Body, heading (Kalam in Playful), mono, heading shorthands | | Shape | `--r-ctl` `--r-btn` `--r-box` `--r-tag` | Controls, buttons (pill), boxes, tags | | Shadow | `--sh-box` `--sh-btn` `--sh-float` `--sh-dialog` | Boxes, buttons, menus and toasts, dialogs | Tailwind names: `bg` `side` `surface` `sunken` `ink` `ink-2` `ink-3` `line` `line-2` `line-soft` `sel` `pri` `on-pri` `ok` `warn` `bad` (with `-bg`) and `link` / `link-bg` for `--accent`. **Legacy names**: `pencil`, `pencil-light`, `paper`, `paper-warm`, `muted`, `muted-dark`, `success`, `warning`, `danger`, `blue`, `info`, `accent` still resolve as aliases for markup not yet migrated. Do not use them in new code. When you touch a line that has one, replace it: | Legacy | Use | |--------|-----| | `text-pencil` | `text-ink` | | `text-pencil-light` | `text-ink-2` | | `text-muted-dark` | `text-ink-3` | | `bg-paper` / `bg-paper-warm` | `bg-bg` / `bg-side` | | `border-muted` | `border-line` | | `text-success` / `text-warning` / `text-danger` | `text-ok` / `text-warn` / `text-bad` | | `text-blue` / `text-info` | `text-link` | `ui/src/design.ts` (`radius`, `shadows`, `palette`) forwards to the same variables, for inline styles only. ### The `ss-*` classes All in `@layer components` in `ui/src/components.css`. List what exists today: ```bash grep -o '\.ss-[a-z0-9-]*' ui/src/components.css | sort -u ``` State and variant are short modifier classes on the same element: `.on` (selected or checked), `.sel` (selected row or tile), `.ok` `.warn` `.bad` `.inf` (tone), `.sm` `.lg` (size). | Area | Classes | |------|---------| | Page | `.ss-wrap` (1080px column, 28px gap), `.ss-pgh` + `.ss-ph` (header, via `PageHeader`), `.ss-crumb`, `.ss-sec` (section heading row; `h2` inside, `.more` link on the right), `.ss-h1` `.ss-h2`, `.ss-hand` (Kalam aside) | | Shell | `.ss-side` `.ss-wm` `.ss-nvg` `.ss-nv` `.ss-sidefoot` — `Layout.tsx` only | | Buttons | `.ss-btn` + `.pri` `.ghost` `.dng` + `.sm` `.lg`; `.ss-ib` (30px icon button); `.ss-more` (text link) | | Forms | `.ss-fld` (label + control + `.hp` help), `.ss-inp` (+ `.area` `.err`, `.k` key hint), `.ss-chk` (+ `.rad`), `.ss-sw` (switch; `.on`, `.mix` when only some are on), `.ss-tgl` (icon toggle), `.ss-seg` (+ `.ic` icon-only) | | Navigation | `.ss-tabs`, `.ss-tabbar` (tabs with controls on the right), `.ss-pager`, `.ss-menu` (+ `.hv` `.dng`, `hr`, `.k`) | | Lists | `.ss-list` (framed container), `.ss-lh` (column header), `.ss-gh` (group header), `.ss-r` (row; `.link` clickable, `.sel`, `.fold`; `.nm` name, `.nm.m` mono name), `.ss-plain` (rows without side padding), `.ss-split` (tree view box: `.lp` tree, `.dv` divider, `.rp` detail pane), `.ss-tn` (tree row; `.sel`, `.in` inside a selected folder, `.off`; indent with `--d`) | | Boxes | `.ss-box` (card, via `Card`), `.ss-tiles` + `.ss-tile` (grid; sticky notes in Playful), `.ss-kv` (`dl` key/value), `.ss-setrow` (settings row), `.ss-counts` (stat strip) | | Status | `.ss-st` (dot + text; `.ok` `.warn` `.bad` `.off`, `.wrap` for long messages), `.ss-tag` (mono label; `.ok` `.warn` `.bad` `.inf`), `.ss-sev` (audit severity; `.c` `.h` `.md` `.l` `.n`), `.ss-cnt` (count) | | Icons | `.ss-cat` (kind tile; `.skill` `.agent` `.extra` `.mcp` `.plugin` `.target`, tones, `.sm`), `.ss-at` (agent or tool logo; `.lg`), `.ss-stack` (overlapping logos) | | Feedback | `.ss-note` (+ `.warn` `.bad` `.inf`), `.ss-empty` (via `EmptyState`), `.ss-prog`, `.ss-skel`, `.ss-toast`, `.ss-tip` | | Overlays | `.ss-scrim` + `.ss-dlg` with `.dh` `.db` `.df` (via `DialogShell`), `.ss-bulk` (selection toolbar), `.ss-top` | | Content | `.ss-prose` (rendered markdown), `.ss-code` (+ `.ln` `.cur`), `.ss-pre`, `.ss-ed` (editor) | | Dashboard | `.ss-board` `.ss-pin` `.ss-pinnote` `.ss-squig` — Playful pin board | Tailwind utilities are for layout inside these (`flex`, `gap-*`, `min-w-0`, `w-[92px]`, `truncate`). Colour, border, radius and shadow come from `ss-*` classes or token utilities. **Cascade gotcha**: the `ss-*` classes sit in `@layer components`, so a Tailwind utility on the same element always wins, whatever the selector specificity. Do not put `mb-*` on something `.ss-wrap` already spaces. To override an `ss-*` property from markup, use the Tailwind important prefix, as in `className="ss-r link !min-h-[56px]"`. ### Page structure ```tsx
` in MDX must be written `~`.
---
## Anti-patterns
| Don't | Do instead |
|-------|------------|
| Hardcoded hex, radius, shadow or font in a page | Token utility (`text-ink-2`) or `ss-*` class |
| Legacy names (`text-pencil-light`, `border-muted`, `text-danger`) in new code | `text-ink-2`, `border-line`, `text-bad` |
| Root `` | `.ss-wrap` |
| Hand-rolled dashed separators | `.ss-r` inside `.ss-list`, or `border: var(--sep)` |
| ``, bare `` or `` for app content | `.ss-list` rows, `.ss-note bad` |
| Playful pastels (`--pa`…`--pe`) in a shared rule | Scope to `:root[data-theme="playful"]` |
| Tilted cards | Nothing tilts in the dashboard. Rotation exists only on the website homepage |
| Dot and tag both carrying the same status | One per element: `.ss-st` for state, `.ss-tag` for a label |
| Left colour stripes (`border-l-*`) | `.ss-st`, `.ss-tag` or `.ss-note` |
| Emoji as icons | lucide, or `AgentIcon` for real tools |
| Stat cards for one to three numbers | Inline text, or `.ss-cnt` beside the heading |
| `window.confirm()` | `ConfirmDialog` |
| Custom empty-state markup | `EmptyState` |
| A small checkbox or icon as the only click target in a clickable row | Keep the hit area at 24px or more; `.ss-chk` already does this with `::after` |
| Dropdown inside a `Card` getting clipped | `overflow` prop on `Card` |
| Wording that drifts from the CLI | Use the CLI's terms: sync, target, collect, tracked, merge |
## Checklist
- [ ] Root is `.ss-wrap animate-fade-in`; `PageHeader` first, without `icon`
- [ ] No hardcoded colours, radii or shadows; no legacy token names added
- [ ] Shared components used where they exist; lists are `.ss-list` / `.ss-r`
- [ ] Errors are `.ss-note bad`, empty states are `EmptyState`, destructive actions go through `ConfirmDialog`
- [ ] Icon-only buttons have an accessible name; click targets are 24px or more
- [ ] Strings go through `useT()`; new shortcuts are added to `useGlobalShortcuts.ts`
- [ ] Screenshots checked in Clean and Playful, light and dark