@uidu/skills 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/package.json +1 -1
- package/skills/uidu/SKILL.md +1 -1
- package/skills/uidu-design/SKILL.md +136 -0
- package/skills/uidu-design/metadata.json +8 -0
package/README.md
CHANGED
|
@@ -14,9 +14,10 @@ Replace `claude-code` with your agent of choice (`cursor`, `copilot`, `windsurf`
|
|
|
14
14
|
|
|
15
15
|
## Available skills
|
|
16
16
|
|
|
17
|
-
| Skill
|
|
18
|
-
|
|
19
|
-
| `uidu`
|
|
17
|
+
| Skill | Description |
|
|
18
|
+
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `uidu` | Comprehensive guide to the uidu SDK — package map, env setup, fetch patterns for CMS pages, events, stories, donations, help center, and forms, plus RichText rendering and scaffolding via `create-uidu-app`. |
|
|
20
|
+
| `uidu-design` | How an app framed inside uidu (a custom app) should look: uidu's tokens and components, layout inside the iframe, states, icons. Ships inside the `custom-app` template, so the app builder's agent has it too. |
|
|
20
21
|
|
|
21
22
|
More skills will be split out from `uidu` as individual areas grow.
|
|
22
23
|
|
package/package.json
CHANGED
package/skills/uidu/SKILL.md
CHANGED
|
@@ -164,7 +164,7 @@ export function Bookings() {
|
|
|
164
164
|
}
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
-
`app.context` carries `user`, `space`, `workspaceApp`, `locale` and `
|
|
167
|
+
`app.context` carries `user`, `space`, `workspaceApp`, `locale`, `theme` and `accent` (the page's colour, to set as `--primary`). Outside React: `connect()` from
|
|
168
168
|
`@uidu/app-bridge`, then `createClient(fromBridge(bridge))` from `@uidu/client`. Start a new
|
|
169
169
|
one with `npm create uidu-app@latest my-app -- -t custom-app`: it declares its Models in
|
|
170
170
|
`public/uidu.app.json` and sets `frame-ancestors` so only uidu can frame it.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: uidu-design
|
|
3
|
+
description: Use when building or restyling the UI of an app that runs inside uidu (a custom app framed in a Space or workspace, including one made in the uidu app builder) — layout, components, colours, typography, empty and loading states. Makes the app look like the uidu page around it instead of a patch sewn on.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: uidu
|
|
7
|
+
version: '0.1.0'
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Looking like uidu
|
|
11
|
+
|
|
12
|
+
A custom app is an iframe in the middle of a uidu page. Above it sits uidu's header, with
|
|
13
|
+
the app's name and its buttons; around it, uidu's sidebar. Whatever the app draws has those
|
|
14
|
+
on every side, so the only good outcome is an app that reads as one more uidu page.
|
|
15
|
+
|
|
16
|
+
That is mostly already done for you. The template ships **uidu's own tokens**
|
|
17
|
+
(`src/app/globals.css`) and **uidu's own components** (`src/components/ui/`), generated from
|
|
18
|
+
uidu's source. The rules below are about not undoing it.
|
|
19
|
+
|
|
20
|
+
## 1. Use the kit, don't restyle it
|
|
21
|
+
|
|
22
|
+
- Build from `src/components/ui/*`: `Button`, `Input`, `Field`, `Select`, `Checkbox`,
|
|
23
|
+
`Switch`, `Textarea`, `Table`, `Tabs`, `Dialog`, `Sheet`, `DropdownMenu`, `Popover`,
|
|
24
|
+
`Tooltip`, `Badge`, `Alert`, `Empty`, `Skeleton`, `Spinner`, `Item`, `Card`, `Calendar`,
|
|
25
|
+
`Toaster` (`toast()` from `sonner`)…
|
|
26
|
+
- A `<button>`, `<input>`, `<table>` or `<select>` written by hand is almost always a
|
|
27
|
+
component you didn't import. A `<div className="rounded-lg border p-4">` is a `Card`, a
|
|
28
|
+
coloured pill is a `Badge`, a status line is an `Alert`.
|
|
29
|
+
- Don't edit the files in `src/components/ui/`: they are regenerated from uidu. Compose them,
|
|
30
|
+
and pass `className` for layout (margins, width, flex), not for colour or size.
|
|
31
|
+
- Need one that isn't there? `npx shadcn add @uidu/<name>` works where the network allows it
|
|
32
|
+
(not in the builder's sandbox). Otherwise build it from the ones you have.
|
|
33
|
+
|
|
34
|
+
## 2. Colours are tokens, never a palette
|
|
35
|
+
|
|
36
|
+
Only the semantic classes, which flip with uidu's light/dark theme:
|
|
37
|
+
|
|
38
|
+
| For | Use |
|
|
39
|
+
| -------------------------------------------------- | ------------------------------------------------------------- |
|
|
40
|
+
| page, text | `bg-background`, `text-foreground` |
|
|
41
|
+
| secondary text, captions, labels | `text-muted-foreground` |
|
|
42
|
+
| panels, popovers | `bg-card`, `bg-popover` (usually via `Card`, `Popover`) |
|
|
43
|
+
| hover, selected row | `bg-accent`, `hover:bg-accent` |
|
|
44
|
+
| subtle fill (a weekend, a disabled row) | `bg-muted` |
|
|
45
|
+
| the app's accent (main button, active item, links) | `bg-primary`, `text-primary` |
|
|
46
|
+
| error, destructive action | `text-destructive`, `bg-destructive/10` |
|
|
47
|
+
| done / needs attention / neutral info | `text-success`, `text-warning`, `text-info` (and `/10` fills) |
|
|
48
|
+
| lines | `border` (already the right colour), `divide-y` |
|
|
49
|
+
| charts | `bg-chart-1` … `bg-chart-5`, `var(--chart-1)` |
|
|
50
|
+
|
|
51
|
+
Never `text-gray-*`, `bg-white`, `text-black`, `bg-blue-500`, `text-red-600`, a hex value or
|
|
52
|
+
an inline `style={{ color }}`: they don't follow the theme, and in dark mode they break.
|
|
53
|
+
`--primary` is the page's accent: uidu sends it (`context.accent`, set by
|
|
54
|
+
`app-provider.tsx`), so it changes with the workspace and the app. Don't set it yourself,
|
|
55
|
+
and don't add a brand colour of your own — `bg-primary` already is one.
|
|
56
|
+
|
|
57
|
+
## 3. Typography: uidu's density
|
|
58
|
+
|
|
59
|
+
- Body text is `text-sm` (14px): paragraphs, table cells, list items, form fields.
|
|
60
|
+
- `text-base` only for emphasis: the figure a card is about, a record's name.
|
|
61
|
+
- `text-xs` is rare: counters, hints.
|
|
62
|
+
- Headings inside the app are `text-sm font-semibold` (a section) or `text-base
|
|
63
|
+
font-semibold` at most. No `text-2xl` hero titles: this is a tool, not a landing page.
|
|
64
|
+
- A label never outweighs its value: label `text-sm text-muted-foreground`, value
|
|
65
|
+
`font-medium`.
|
|
66
|
+
- The font is Inter, set in `layout.tsx`. Don't load another one.
|
|
67
|
+
|
|
68
|
+
## 4. Layout: fill the frame, don't frame it again
|
|
69
|
+
|
|
70
|
+
- **No title that repeats the app's name.** uidu's header already shows it. If the page needs
|
|
71
|
+
a bar, make it a toolbar: `flex h-14 shrink-0 items-center justify-between gap-2 border-b
|
|
72
|
+
px-4` with context (a count, a filter, a period) on the left and actions on the right.
|
|
73
|
+
- **No second navigation shell**: no sidebar, no top nav, no footer, no logo. For a few views
|
|
74
|
+
use `Tabs` in the toolbar; for a detail, a `Sheet` or a `Dialog`.
|
|
75
|
+
- **Edge to edge.** The page fills the iframe (`flex min-h-screen flex-col`): no centred
|
|
76
|
+
`max-w-2xl` column with a card floating in it, no outer margin. Content is inset `px-4`
|
|
77
|
+
(16px), the same as uidu's header above, so their left edges line up.
|
|
78
|
+
- Sections are separated by `border-b`, not by stacking cards with gaps. Use `Card` for a
|
|
79
|
+
real sub-surface (one card per related thing), never a card inside a card.
|
|
80
|
+
- **No dead space.** When the data is short, the empty area is filled by an `Empty` state
|
|
81
|
+
(it grows with `flex-1`) or the page ends where the data ends. Never leave the bottom
|
|
82
|
+
third of the frame blank.
|
|
83
|
+
- Spacing on Tailwind's scale (`gap-2`, `gap-3`, `p-4`, `px-4`); no `px-[13px]`.
|
|
84
|
+
|
|
85
|
+
## 5. Data
|
|
86
|
+
|
|
87
|
+
- Rows of things are a `Table` (`TableHeader`/`TableHead`/`TableBody`/`TableRow`/`TableCell`),
|
|
88
|
+
with `pl-4` on the first column and `pr-4` on the last, to align with the toolbar.
|
|
89
|
+
- A table that scrolls keeps its header visible: bound the table's own container with
|
|
90
|
+
`containerClassName="h-full overflow-y-auto"` and make the `TableHead`s `sticky top-0
|
|
91
|
+
bg-background`. Wrapping the table in your own scroller does not work.
|
|
92
|
+
- Dates and numbers through `Intl` with `context.locale` (`toLocaleString(locale)`,
|
|
93
|
+
`Intl.NumberFormat(locale, …)`), never hand-formatted.
|
|
94
|
+
- A thing that spans several days or columns is **one** element across them, not one per cell.
|
|
95
|
+
|
|
96
|
+
## 6. States
|
|
97
|
+
|
|
98
|
+
Every screen has four, and each has a shape:
|
|
99
|
+
|
|
100
|
+
| State | Shape |
|
|
101
|
+
| -------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
102
|
+
| connecting / loading | `Skeleton`s shaped like the content (rows, a card) — not a centred spinner, not "Loading…" text alone |
|
|
103
|
+
| empty | `Empty` with an icon (`EmptyMedia variant="icon"`), a title, one sentence and, if there is one, the action |
|
|
104
|
+
| error | `Alert variant="destructive"` near what failed, with what to do; a failed write keeps what the person typed |
|
|
105
|
+
| saving | the button stays, disabled, with a `Spinner` inside; `toast()` for a result that lands elsewhere |
|
|
106
|
+
|
|
107
|
+
## 7. Icons
|
|
108
|
+
|
|
109
|
+
- `lucide-react` only, imported by name: `import { Plus } from 'lucide-react'`.
|
|
110
|
+
- Inside a component (`Button`, `DropdownMenuItem`, `Badge`, `Alert`, `EmptyMedia`…) write
|
|
111
|
+
`<Plus />` with **no** `size-*` class: the component sizes it. Elsewhere `size-4`.
|
|
112
|
+
- Never change `strokeWidth`. Colour with `text-*` tokens.
|
|
113
|
+
- An icon-only button is `variant="ghost" size="icon"` with an `aria-label`, and the icon
|
|
114
|
+
`aria-hidden="true"`.
|
|
115
|
+
|
|
116
|
+
## 8. Accessible by default
|
|
117
|
+
|
|
118
|
+
- Every field has a label (`Field` + `FieldLabel htmlFor`), every icon-only button an
|
|
119
|
+
`aria-label`.
|
|
120
|
+
- Actions are buttons, navigation is links; nothing clickable is a `<div onClick>`.
|
|
121
|
+
- Messages that appear after an action (`Alert`, a saved state) are announced: `role="alert"`
|
|
122
|
+
for errors, `aria-live="polite"` for the rest.
|
|
123
|
+
- Motion stays small (the components' own); spatial animations get `motion-safe:`.
|
|
124
|
+
|
|
125
|
+
## 9. Words
|
|
126
|
+
|
|
127
|
+
- Short, plain, in the person's language (`context.locale`); sentence case ("New booking",
|
|
128
|
+
not "New Booking").
|
|
129
|
+
- Say what happened and what to do next, not an error code.
|
|
130
|
+
|
|
131
|
+
## Before you finish
|
|
132
|
+
|
|
133
|
+
Run `npm run check`: it type-checks and flags raw palette colours, hex values, hand-written
|
|
134
|
+
`<button>`/`<input>`/`<table>`, other icon sets and `strokeWidth`. Fix what it reports.
|
|
135
|
+
Then look at the page in dark mode too: if something is invisible or glaring there, it is
|
|
136
|
+
using a colour instead of a token.
|