@layoutdesign/context 0.1.7
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/LICENSE +21 -0
- package/README.md +424 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +57 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/src/cli/import-zip.d.ts +2 -0
- package/dist/src/cli/import-zip.d.ts.map +1 -0
- package/dist/src/cli/import-zip.js +156 -0
- package/dist/src/cli/import-zip.js.map +1 -0
- package/dist/src/cli/init.d.ts +4 -0
- package/dist/src/cli/init.d.ts.map +1 -0
- package/dist/src/cli/init.js +104 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/install.d.ts +5 -0
- package/dist/src/cli/install.d.ts.map +1 -0
- package/dist/src/cli/install.js +192 -0
- package/dist/src/cli/install.js.map +1 -0
- package/dist/src/cli/list.d.ts +2 -0
- package/dist/src/cli/list.d.ts.map +1 -0
- package/dist/src/cli/list.js +36 -0
- package/dist/src/cli/list.js.map +1 -0
- package/dist/src/cli/serve.d.ts +2 -0
- package/dist/src/cli/serve.d.ts.map +1 -0
- package/dist/src/cli/serve.js +9 -0
- package/dist/src/cli/serve.js.map +1 -0
- package/dist/src/cli/use.d.ts +2 -0
- package/dist/src/cli/use.d.ts.map +1 -0
- package/dist/src/cli/use.js +54 -0
- package/dist/src/cli/use.js.map +1 -0
- package/dist/src/compliance/checker.d.ts +23 -0
- package/dist/src/compliance/checker.d.ts.map +1 -0
- package/dist/src/compliance/checker.js +31 -0
- package/dist/src/compliance/checker.js.map +1 -0
- package/dist/src/compliance/rules.d.ts +11 -0
- package/dist/src/compliance/rules.d.ts.map +1 -0
- package/dist/src/compliance/rules.js +147 -0
- package/dist/src/compliance/rules.js.map +1 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +6 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/kit/loader.d.ts +16 -0
- package/dist/src/kit/loader.d.ts.map +1 -0
- package/dist/src/kit/loader.js +98 -0
- package/dist/src/kit/loader.js.map +1 -0
- package/dist/src/kit/parser.d.ts +21 -0
- package/dist/src/kit/parser.d.ts.map +1 -0
- package/dist/src/kit/parser.js +98 -0
- package/dist/src/kit/parser.js.map +1 -0
- package/dist/src/kit/registry.d.ts +4 -0
- package/dist/src/kit/registry.d.ts.map +1 -0
- package/dist/src/kit/registry.js +91 -0
- package/dist/src/kit/registry.js.map +1 -0
- package/dist/src/kit/types.d.ts +51 -0
- package/dist/src/kit/types.d.ts.map +1 -0
- package/dist/src/kit/types.js +11 -0
- package/dist/src/kit/types.js.map +1 -0
- package/dist/src/mcp/server.d.ts +6 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +56 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tools/check-compliance.d.ts +16 -0
- package/dist/src/mcp/tools/check-compliance.d.ts.map +1 -0
- package/dist/src/mcp/tools/check-compliance.js +44 -0
- package/dist/src/mcp/tools/check-compliance.js.map +1 -0
- package/dist/src/mcp/tools/design-in-figma.d.ts +24 -0
- package/dist/src/mcp/tools/design-in-figma.d.ts.map +1 -0
- package/dist/src/mcp/tools/design-in-figma.js +202 -0
- package/dist/src/mcp/tools/design-in-figma.js.map +1 -0
- package/dist/src/mcp/tools/get-component.d.ts +16 -0
- package/dist/src/mcp/tools/get-component.d.ts.map +1 -0
- package/dist/src/mcp/tools/get-component.js +52 -0
- package/dist/src/mcp/tools/get-component.js.map +1 -0
- package/dist/src/mcp/tools/get-design-system.d.ts +16 -0
- package/dist/src/mcp/tools/get-design-system.d.ts.map +1 -0
- package/dist/src/mcp/tools/get-design-system.js +51 -0
- package/dist/src/mcp/tools/get-design-system.js.map +1 -0
- package/dist/src/mcp/tools/get-screenshots.d.ts +23 -0
- package/dist/src/mcp/tools/get-screenshots.d.ts.map +1 -0
- package/dist/src/mcp/tools/get-screenshots.js +78 -0
- package/dist/src/mcp/tools/get-screenshots.js.map +1 -0
- package/dist/src/mcp/tools/get-tokens.d.ts +20 -0
- package/dist/src/mcp/tools/get-tokens.d.ts.map +1 -0
- package/dist/src/mcp/tools/get-tokens.js +50 -0
- package/dist/src/mcp/tools/get-tokens.js.map +1 -0
- package/dist/src/mcp/tools/list-components.d.ts +11 -0
- package/dist/src/mcp/tools/list-components.d.ts.map +1 -0
- package/dist/src/mcp/tools/list-components.js +38 -0
- package/dist/src/mcp/tools/list-components.js.map +1 -0
- package/dist/src/mcp/tools/preview.d.ts +21 -0
- package/dist/src/mcp/tools/preview.d.ts.map +1 -0
- package/dist/src/mcp/tools/preview.js +63 -0
- package/dist/src/mcp/tools/preview.js.map +1 -0
- package/dist/src/mcp/tools/push-to-figma.d.ts +24 -0
- package/dist/src/mcp/tools/push-to-figma.d.ts.map +1 -0
- package/dist/src/mcp/tools/push-to-figma.js +101 -0
- package/dist/src/mcp/tools/push-to-figma.js.map +1 -0
- package/dist/src/mcp/tools/update-tokens.d.ts +21 -0
- package/dist/src/mcp/tools/update-tokens.d.ts.map +1 -0
- package/dist/src/mcp/tools/update-tokens.js +187 -0
- package/dist/src/mcp/tools/update-tokens.js.map +1 -0
- package/dist/src/mcp/tools/url-to-figma.d.ts +29 -0
- package/dist/src/mcp/tools/url-to-figma.d.ts.map +1 -0
- package/dist/src/mcp/tools/url-to-figma.js +103 -0
- package/dist/src/mcp/tools/url-to-figma.js.map +1 -0
- package/dist/src/preview/server.d.ts +15 -0
- package/dist/src/preview/server.d.ts.map +1 -0
- package/dist/src/preview/server.js +146 -0
- package/dist/src/preview/server.js.map +1 -0
- package/dist/src/preview/static/index.html +493 -0
- package/dist/src/preview/transpile.d.ts +10 -0
- package/dist/src/preview/transpile.d.ts.map +1 -0
- package/dist/src/preview/transpile.js +40 -0
- package/dist/src/preview/transpile.js.map +1 -0
- package/dist/src/preview/ws.d.ts +17 -0
- package/dist/src/preview/ws.d.ts.map +1 -0
- package/dist/src/preview/ws.js +66 -0
- package/dist/src/preview/ws.js.map +1 -0
- package/kits/linear-lite/DESIGN.md +421 -0
- package/kits/linear-lite/kit.json +12 -0
- package/kits/linear-lite/tokens.css +46 -0
- package/kits/linear-lite/tokens.json +47 -0
- package/kits/notion-lite/DESIGN.md +528 -0
- package/kits/notion-lite/kit.json +12 -0
- package/kits/notion-lite/tokens.css +50 -0
- package/kits/notion-lite/tokens.json +51 -0
- package/kits/stripe-lite/DESIGN.md +539 -0
- package/kits/stripe-lite/kit.json +12 -0
- package/kits/stripe-lite/tokens.css +57 -0
- package/kits/stripe-lite/tokens.json +58 -0
- package/package.json +63 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
# Notion — Design System
|
|
2
|
+
|
|
3
|
+
> Content-first. The UI disappears so the writing can breathe.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Quick Reference
|
|
8
|
+
|
|
9
|
+
**Aesthetic:** Warm off-whites, organic warm-gray text, and almost-invisible borders. The interface is subordinate to content — controls only appear when needed. Typography is the primary design element: comfortable line-height, generous max-width, and subtle serif option for long-form content.
|
|
10
|
+
|
|
11
|
+
### Colour Palette
|
|
12
|
+
| Role | Token | Value |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| App background | `--notion-bg-app` | `#FFFFFF` |
|
|
15
|
+
| Surface (sidebars, panels) | `--notion-bg-surface` | `#F7F6F3` |
|
|
16
|
+
| Hover state | `--notion-bg-hover` | `#EEEEE9` |
|
|
17
|
+
| Selected state | `--notion-bg-selected` | `#E8E7E3` |
|
|
18
|
+
| Border (default) | `--notion-border` | `rgba(55,53,47,0.09)` |
|
|
19
|
+
| Border (strong) | `--notion-border-strong` | `rgba(55,53,47,0.18)` |
|
|
20
|
+
| Primary text | `--notion-text-primary` | `#37352F` |
|
|
21
|
+
| Secondary text | `--notion-text-secondary` | `#787774` |
|
|
22
|
+
| Muted text | `--notion-text-muted` | `#9B9A97` |
|
|
23
|
+
| Placeholder | `--notion-text-placeholder` | `rgba(55,53,47,0.4)` |
|
|
24
|
+
| Accent | `--notion-accent` | `#2383E2` |
|
|
25
|
+
| Accent hover | `--notion-accent-hover` | `#1E78D3` |
|
|
26
|
+
| Accent subtle | `--notion-accent-subtle` | `rgba(35,131,226,0.1)` |
|
|
27
|
+
| Callout: blue | `--notion-callout-blue` | `rgba(35,131,226,0.1)` |
|
|
28
|
+
| Callout: yellow | `--notion-callout-yellow` | `rgba(255,184,0,0.14)` |
|
|
29
|
+
| Callout: green | `--notion-callout-green` | `rgba(15,123,108,0.1)` |
|
|
30
|
+
| Callout: red | `--notion-callout-red` | `rgba(235,87,87,0.1)` |
|
|
31
|
+
| Callout: gray | `--notion-callout-gray` | `rgba(55,53,47,0.06)` |
|
|
32
|
+
|
|
33
|
+
### Typography
|
|
34
|
+
- **UI font:** `ui-sans-serif`, -apple-system, BlinkMacSystemFont, Segoe UI — via `--notion-font-sans`
|
|
35
|
+
- **Content font (optional):** `Georgia`, Times New Roman — via `--notion-font-serif`
|
|
36
|
+
- **Code font:** `SFMono-Regular`, Menlo, Consolas — via `--notion-font-mono`
|
|
37
|
+
- Body text: 16px / 1.7 line-height — generous for reading
|
|
38
|
+
- UI labels: 14px, `--notion-text-secondary`
|
|
39
|
+
- Small labels: 12px, `--notion-text-muted`
|
|
40
|
+
- Max content width: 720px (full width: none, centred)
|
|
41
|
+
|
|
42
|
+
### Spacing Scale
|
|
43
|
+
`2 / 4 / 8 / 16 / 24 / 32px` — tokens `2xs / xs / sm / md / lg / xl`
|
|
44
|
+
|
|
45
|
+
### Border Radius
|
|
46
|
+
`3px (sm) / 4px (md) / 6px (lg)` — intentionally small, organic
|
|
47
|
+
|
|
48
|
+
### Key Design Rules
|
|
49
|
+
1. **The background is white.** `--notion-bg-app` = `#FFFFFF`. No dark backgrounds.
|
|
50
|
+
2. **Warm grays, not cool.** All neutrals have a warm brownish undertone (`#37352F`).
|
|
51
|
+
3. **Borders are opacity-based** on `#37352F` — they adapt to any background tint.
|
|
52
|
+
4. **Hover is a background change** only — no border or shadow change.
|
|
53
|
+
5. **No box-shadow on interactive elements.** Only use shadow (if at all) on floating elements like menus.
|
|
54
|
+
6. **Accent (blue) is for links and active states only.** Never fill a button with it except for a true CTA.
|
|
55
|
+
7. **Icons appear on hover** — ghost by default, visible when the user approaches.
|
|
56
|
+
8. **Transitions:** 100ms ease-out. Fast enough to feel instant.
|
|
57
|
+
9. **Callout colours** are always paired: a tinted background from `--notion-callout-*` with the appropriately-toned text.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Design Tokens
|
|
62
|
+
|
|
63
|
+
### Backgrounds
|
|
64
|
+
```
|
|
65
|
+
--notion-bg-app: #FFFFFF
|
|
66
|
+
--notion-bg-surface: #F7F6F3
|
|
67
|
+
--notion-bg-hover: #EEEEE9
|
|
68
|
+
--notion-bg-selected: #E8E7E3
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Borders
|
|
72
|
+
```
|
|
73
|
+
--notion-border: rgba(55, 53, 47, 0.09)
|
|
74
|
+
--notion-border-strong: rgba(55, 53, 47, 0.18)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Text
|
|
78
|
+
```
|
|
79
|
+
--notion-text-primary: #37352F
|
|
80
|
+
--notion-text-secondary: #787774
|
|
81
|
+
--notion-text-muted: #9B9A97
|
|
82
|
+
--notion-text-placeholder: rgba(55, 53, 47, 0.4)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Accent
|
|
86
|
+
```
|
|
87
|
+
--notion-accent: #2383E2
|
|
88
|
+
--notion-accent-hover: #1E78D3
|
|
89
|
+
--notion-accent-subtle: rgba(35, 131, 226, 0.1)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Callout Backgrounds
|
|
93
|
+
```
|
|
94
|
+
--notion-callout-blue: rgba(35, 131, 226, 0.1)
|
|
95
|
+
--notion-callout-yellow: rgba(255, 184, 0, 0.14)
|
|
96
|
+
--notion-callout-green: rgba(15, 123, 108, 0.1)
|
|
97
|
+
--notion-callout-red: rgba(235, 87, 87, 0.1)
|
|
98
|
+
--notion-callout-gray: rgba(55, 53, 47, 0.06)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Typography
|
|
102
|
+
```
|
|
103
|
+
--notion-font-sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif
|
|
104
|
+
--notion-font-serif: Georgia, 'Times New Roman', serif
|
|
105
|
+
--notion-font-mono: 'SFMono-Regular', Menlo, Consolas, monospace
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Spacing
|
|
109
|
+
```
|
|
110
|
+
--notion-space-2xs: 2px
|
|
111
|
+
--notion-space-xs: 4px
|
|
112
|
+
--notion-space-sm: 8px
|
|
113
|
+
--notion-space-md: 16px
|
|
114
|
+
--notion-space-lg: 24px
|
|
115
|
+
--notion-space-xl: 32px
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Border Radius
|
|
119
|
+
```
|
|
120
|
+
--notion-radius-sm: 3px
|
|
121
|
+
--notion-radius-md: 4px
|
|
122
|
+
--notion-radius-lg: 6px
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Components
|
|
128
|
+
|
|
129
|
+
### Button
|
|
130
|
+
|
|
131
|
+
Minimal ghost-style buttons by default. The "primary" variant is used sparingly — only for true CTAs like "Create" or "Save". Text buttons are the standard. Height 28px, tight padding, barely-there hover.
|
|
132
|
+
|
|
133
|
+
**Tokens used:** `--notion-bg-hover`, `--notion-bg-selected`, `--notion-text-primary`, `--notion-text-secondary`, `--notion-accent`, `--notion-accent-subtle`, `--notion-radius-sm`
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
type ButtonVariant = 'primary' | 'ghost' | 'text';
|
|
137
|
+
|
|
138
|
+
interface ButtonProps {
|
|
139
|
+
variant?: ButtonVariant;
|
|
140
|
+
children: React.ReactNode;
|
|
141
|
+
onClick?: () => void;
|
|
142
|
+
disabled?: boolean;
|
|
143
|
+
size?: 'sm' | 'md';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function Button({
|
|
147
|
+
variant = 'ghost',
|
|
148
|
+
children,
|
|
149
|
+
onClick,
|
|
150
|
+
disabled,
|
|
151
|
+
size = 'md',
|
|
152
|
+
}: ButtonProps) {
|
|
153
|
+
const height = size === 'sm' ? '24px' : '28px';
|
|
154
|
+
const fontSize = size === 'sm' ? '12px' : '14px';
|
|
155
|
+
const padding = size === 'sm' ? '0 8px' : '0 10px';
|
|
156
|
+
|
|
157
|
+
const base: React.CSSProperties = {
|
|
158
|
+
display: 'inline-flex',
|
|
159
|
+
alignItems: 'center',
|
|
160
|
+
gap: '4px',
|
|
161
|
+
height,
|
|
162
|
+
padding,
|
|
163
|
+
borderRadius: 'var(--notion-radius-sm)',
|
|
164
|
+
fontSize,
|
|
165
|
+
fontWeight: 500,
|
|
166
|
+
fontFamily: 'var(--notion-font-sans)',
|
|
167
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
168
|
+
opacity: disabled ? 0.5 : 1,
|
|
169
|
+
border: 'none',
|
|
170
|
+
transition: 'background 100ms ease-out',
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const variants: Record<ButtonVariant, React.CSSProperties> = {
|
|
174
|
+
primary: {
|
|
175
|
+
background: 'var(--notion-text-primary)',
|
|
176
|
+
color: '#FFFFFF',
|
|
177
|
+
},
|
|
178
|
+
ghost: {
|
|
179
|
+
background: 'transparent',
|
|
180
|
+
color: 'var(--notion-text-secondary)',
|
|
181
|
+
},
|
|
182
|
+
text: {
|
|
183
|
+
background: 'transparent',
|
|
184
|
+
color: 'var(--notion-accent)',
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const hoverBg: Record<ButtonVariant, string> = {
|
|
189
|
+
primary: 'rgba(55,53,47,0.85)',
|
|
190
|
+
ghost: 'var(--notion-bg-hover)',
|
|
191
|
+
text: 'var(--notion-accent-subtle)',
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<button
|
|
196
|
+
style={{ ...base, ...variants[variant] }}
|
|
197
|
+
onClick={onClick}
|
|
198
|
+
disabled={disabled}
|
|
199
|
+
onMouseEnter={e => {
|
|
200
|
+
if (!disabled) (e.currentTarget as HTMLButtonElement).style.background = hoverBg[variant];
|
|
201
|
+
}}
|
|
202
|
+
onMouseLeave={e => {
|
|
203
|
+
const defaultBg = variant === 'primary' ? 'var(--notion-text-primary)' : 'transparent';
|
|
204
|
+
(e.currentTarget as HTMLButtonElement).style.background = defaultBg;
|
|
205
|
+
}}
|
|
206
|
+
>
|
|
207
|
+
{children}
|
|
208
|
+
</button>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
### TextBlock
|
|
216
|
+
|
|
217
|
+
Editable content block — the core primitive of any Notion-style editor. Renders a `contentEditable` div with Notion's characteristic placeholder behaviour. Uses serif or sans font depending on the content type.
|
|
218
|
+
|
|
219
|
+
**Tokens used:** `--notion-font-sans`, `--notion-font-serif`, `--notion-text-primary`, `--notion-text-placeholder`, `--notion-bg-hover`, `--notion-radius-sm`
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
type BlockType = 'paragraph' | 'heading1' | 'heading2' | 'heading3';
|
|
223
|
+
|
|
224
|
+
interface TextBlockProps {
|
|
225
|
+
type?: BlockType;
|
|
226
|
+
placeholder?: string;
|
|
227
|
+
defaultValue?: string;
|
|
228
|
+
serif?: boolean;
|
|
229
|
+
onFocus?: () => void;
|
|
230
|
+
onBlur?: () => void;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const blockStyles: Record<BlockType, React.CSSProperties> = {
|
|
234
|
+
paragraph: { fontSize: '16px', fontWeight: 400, lineHeight: '1.7' },
|
|
235
|
+
heading1: { fontSize: '30px', fontWeight: 700, lineHeight: '1.3' },
|
|
236
|
+
heading2: { fontSize: '24px', fontWeight: 700, lineHeight: '1.35' },
|
|
237
|
+
heading3: { fontSize: '20px', fontWeight: 600, lineHeight: '1.4' },
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
export function TextBlock({
|
|
241
|
+
type = 'paragraph',
|
|
242
|
+
placeholder = 'Type something…',
|
|
243
|
+
defaultValue,
|
|
244
|
+
serif,
|
|
245
|
+
onFocus,
|
|
246
|
+
onBlur,
|
|
247
|
+
}: TextBlockProps) {
|
|
248
|
+
return (
|
|
249
|
+
<div
|
|
250
|
+
contentEditable
|
|
251
|
+
suppressContentEditableWarning
|
|
252
|
+
data-placeholder={placeholder}
|
|
253
|
+
onFocus={onFocus}
|
|
254
|
+
onBlur={onBlur}
|
|
255
|
+
style={{
|
|
256
|
+
outline: 'none',
|
|
257
|
+
width: '100%',
|
|
258
|
+
color: 'var(--notion-text-primary)',
|
|
259
|
+
fontFamily: serif ? 'var(--notion-font-serif)' : 'var(--notion-font-sans)',
|
|
260
|
+
...blockStyles[type],
|
|
261
|
+
}}
|
|
262
|
+
>
|
|
263
|
+
{defaultValue}
|
|
264
|
+
</div>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Required CSS for placeholder behaviour:
|
|
269
|
+
[contenteditable]:empty::before {
|
|
270
|
+
content: attr(data-placeholder);
|
|
271
|
+
color: var(--notion-text-placeholder);
|
|
272
|
+
pointer-events: none;
|
|
273
|
+
}
|
|
274
|
+
*/
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
### Toggle
|
|
280
|
+
|
|
281
|
+
Expandable toggle block with animated arrow. The summary row has a hover state; the content indents beneath it. A fundamental Notion layout primitive.
|
|
282
|
+
|
|
283
|
+
**Tokens used:** `--notion-bg-hover`, `--notion-text-primary`, `--notion-text-secondary`, `--notion-font-sans`, `--notion-radius-sm`, `--notion-space-md`
|
|
284
|
+
|
|
285
|
+
```tsx
|
|
286
|
+
import { useState } from 'react';
|
|
287
|
+
|
|
288
|
+
interface ToggleProps {
|
|
289
|
+
summary: React.ReactNode;
|
|
290
|
+
children: React.ReactNode;
|
|
291
|
+
defaultOpen?: boolean;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function Toggle({ summary, children, defaultOpen = false }: ToggleProps) {
|
|
295
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
296
|
+
|
|
297
|
+
return (
|
|
298
|
+
<div>
|
|
299
|
+
<div
|
|
300
|
+
onClick={() => setOpen(o => !o)}
|
|
301
|
+
style={{
|
|
302
|
+
display: 'flex',
|
|
303
|
+
alignItems: 'flex-start',
|
|
304
|
+
gap: '4px',
|
|
305
|
+
padding: '2px 4px',
|
|
306
|
+
borderRadius: 'var(--notion-radius-sm)',
|
|
307
|
+
cursor: 'pointer',
|
|
308
|
+
userSelect: 'none',
|
|
309
|
+
transition: 'background 100ms ease-out',
|
|
310
|
+
}}
|
|
311
|
+
onMouseEnter={e => (e.currentTarget.style.background = 'var(--notion-bg-hover)')}
|
|
312
|
+
onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}
|
|
313
|
+
>
|
|
314
|
+
<span
|
|
315
|
+
style={{
|
|
316
|
+
display: 'inline-flex',
|
|
317
|
+
alignItems: 'center',
|
|
318
|
+
justifyContent: 'center',
|
|
319
|
+
width: '22px',
|
|
320
|
+
height: '22px',
|
|
321
|
+
flexShrink: 0,
|
|
322
|
+
color: 'var(--notion-text-muted)',
|
|
323
|
+
transition: 'transform 100ms ease-out',
|
|
324
|
+
transform: open ? 'rotate(90deg)' : 'rotate(0deg)',
|
|
325
|
+
fontSize: '12px',
|
|
326
|
+
marginTop: '2px',
|
|
327
|
+
}}
|
|
328
|
+
>
|
|
329
|
+
▶
|
|
330
|
+
</span>
|
|
331
|
+
<div style={{
|
|
332
|
+
flex: 1,
|
|
333
|
+
fontSize: '16px',
|
|
334
|
+
lineHeight: '1.7',
|
|
335
|
+
color: 'var(--notion-text-primary)',
|
|
336
|
+
fontFamily: 'var(--notion-font-sans)',
|
|
337
|
+
fontWeight: 500,
|
|
338
|
+
}}>
|
|
339
|
+
{summary}
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
{open && (
|
|
344
|
+
<div style={{ paddingLeft: '26px', marginTop: '2px' }}>
|
|
345
|
+
{children}
|
|
346
|
+
</div>
|
|
347
|
+
)}
|
|
348
|
+
</div>
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
### Callout
|
|
356
|
+
|
|
357
|
+
Highlighted block with emoji icon. Five colour variants. Used for tips, warnings, and important notes in documents. The emoji serves as the visual anchor — always provide one.
|
|
358
|
+
|
|
359
|
+
**Tokens used:** `--notion-callout-blue/yellow/green/red/gray`, `--notion-text-primary`, `--notion-radius-md`, `--notion-space-md`
|
|
360
|
+
|
|
361
|
+
```tsx
|
|
362
|
+
type CalloutColour = 'blue' | 'yellow' | 'green' | 'red' | 'gray';
|
|
363
|
+
|
|
364
|
+
interface CalloutProps {
|
|
365
|
+
colour?: CalloutColour;
|
|
366
|
+
emoji?: string;
|
|
367
|
+
children: React.ReactNode;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const calloutBg: Record<CalloutColour, string> = {
|
|
371
|
+
blue: 'var(--notion-callout-blue)',
|
|
372
|
+
yellow: 'var(--notion-callout-yellow)',
|
|
373
|
+
green: 'var(--notion-callout-green)',
|
|
374
|
+
red: 'var(--notion-callout-red)',
|
|
375
|
+
gray: 'var(--notion-callout-gray)',
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const calloutEmoji: Record<CalloutColour, string> = {
|
|
379
|
+
blue: '💡',
|
|
380
|
+
yellow: '⚠️',
|
|
381
|
+
green: '✅',
|
|
382
|
+
red: '🚨',
|
|
383
|
+
gray: '📝',
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
export function Callout({ colour = 'gray', emoji, children }: CalloutProps) {
|
|
387
|
+
return (
|
|
388
|
+
<div
|
|
389
|
+
style={{
|
|
390
|
+
display: 'flex',
|
|
391
|
+
gap: 'var(--notion-space-sm)',
|
|
392
|
+
padding: 'var(--notion-space-md)',
|
|
393
|
+
background: calloutBg[colour],
|
|
394
|
+
borderRadius: 'var(--notion-radius-md)',
|
|
395
|
+
}}
|
|
396
|
+
>
|
|
397
|
+
<span
|
|
398
|
+
style={{
|
|
399
|
+
fontSize: '18px',
|
|
400
|
+
lineHeight: '1.7',
|
|
401
|
+
flexShrink: 0,
|
|
402
|
+
}}
|
|
403
|
+
role="img"
|
|
404
|
+
aria-hidden="true"
|
|
405
|
+
>
|
|
406
|
+
{emoji ?? calloutEmoji[colour]}
|
|
407
|
+
</span>
|
|
408
|
+
<div
|
|
409
|
+
style={{
|
|
410
|
+
flex: 1,
|
|
411
|
+
fontSize: '16px',
|
|
412
|
+
lineHeight: '1.7',
|
|
413
|
+
color: 'var(--notion-text-primary)',
|
|
414
|
+
fontFamily: 'var(--notion-font-sans)',
|
|
415
|
+
}}
|
|
416
|
+
>
|
|
417
|
+
{children}
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
### Breadcrumb
|
|
427
|
+
|
|
428
|
+
Page-path breadcrumb with separator chevrons. Items are clickable links styled as muted text; the last item (current page) is non-interactive and shows in primary text.
|
|
429
|
+
|
|
430
|
+
**Tokens used:** `--notion-text-primary`, `--notion-text-muted`, `--notion-accent`, `--notion-font-sans`, `--notion-bg-hover`, `--notion-radius-sm`
|
|
431
|
+
|
|
432
|
+
```tsx
|
|
433
|
+
interface BreadcrumbItem {
|
|
434
|
+
label: string;
|
|
435
|
+
href?: string;
|
|
436
|
+
onClick?: () => void;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
interface BreadcrumbProps {
|
|
440
|
+
items: BreadcrumbItem[];
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export function Breadcrumb({ items }: BreadcrumbProps) {
|
|
444
|
+
return (
|
|
445
|
+
<nav aria-label="Breadcrumb">
|
|
446
|
+
<ol
|
|
447
|
+
style={{
|
|
448
|
+
display: 'flex',
|
|
449
|
+
alignItems: 'center',
|
|
450
|
+
gap: '2px',
|
|
451
|
+
listStyle: 'none',
|
|
452
|
+
margin: 0,
|
|
453
|
+
padding: 0,
|
|
454
|
+
flexWrap: 'wrap',
|
|
455
|
+
}}
|
|
456
|
+
>
|
|
457
|
+
{items.map((item, index) => {
|
|
458
|
+
const isLast = index === items.length - 1;
|
|
459
|
+
|
|
460
|
+
return (
|
|
461
|
+
<li key={index} style={{ display: 'flex', alignItems: 'center', gap: '2px' }}>
|
|
462
|
+
{isLast ? (
|
|
463
|
+
<span
|
|
464
|
+
style={{
|
|
465
|
+
fontSize: '14px',
|
|
466
|
+
fontWeight: 500,
|
|
467
|
+
color: 'var(--notion-text-primary)',
|
|
468
|
+
fontFamily: 'var(--notion-font-sans)',
|
|
469
|
+
padding: '2px 6px',
|
|
470
|
+
borderRadius: 'var(--notion-radius-sm)',
|
|
471
|
+
}}
|
|
472
|
+
aria-current="page"
|
|
473
|
+
>
|
|
474
|
+
{item.label}
|
|
475
|
+
</span>
|
|
476
|
+
) : (
|
|
477
|
+
<button
|
|
478
|
+
onClick={item.onClick}
|
|
479
|
+
style={{
|
|
480
|
+
fontSize: '14px',
|
|
481
|
+
fontWeight: 400,
|
|
482
|
+
color: 'var(--notion-text-secondary)',
|
|
483
|
+
fontFamily: 'var(--notion-font-sans)',
|
|
484
|
+
background: 'transparent',
|
|
485
|
+
border: 'none',
|
|
486
|
+
padding: '2px 6px',
|
|
487
|
+
borderRadius: 'var(--notion-radius-sm)',
|
|
488
|
+
cursor: item.onClick ? 'pointer' : 'default',
|
|
489
|
+
transition: 'background 100ms ease-out, color 100ms ease-out',
|
|
490
|
+
}}
|
|
491
|
+
onMouseEnter={e => {
|
|
492
|
+
if (!item.onClick) return;
|
|
493
|
+
(e.currentTarget as HTMLButtonElement).style.background = 'var(--notion-bg-hover)';
|
|
494
|
+
(e.currentTarget as HTMLButtonElement).style.color = 'var(--notion-text-primary)';
|
|
495
|
+
}}
|
|
496
|
+
onMouseLeave={e => {
|
|
497
|
+
(e.currentTarget as HTMLButtonElement).style.background = 'transparent';
|
|
498
|
+
(e.currentTarget as HTMLButtonElement).style.color = 'var(--notion-text-secondary)';
|
|
499
|
+
}}
|
|
500
|
+
>
|
|
501
|
+
{item.label}
|
|
502
|
+
</button>
|
|
503
|
+
)}
|
|
504
|
+
{!isLast && (
|
|
505
|
+
<span
|
|
506
|
+
aria-hidden="true"
|
|
507
|
+
style={{
|
|
508
|
+
color: 'var(--notion-text-muted)',
|
|
509
|
+
fontSize: '12px',
|
|
510
|
+
lineHeight: 1,
|
|
511
|
+
userSelect: 'none',
|
|
512
|
+
}}
|
|
513
|
+
>
|
|
514
|
+
/
|
|
515
|
+
</span>
|
|
516
|
+
)}
|
|
517
|
+
</li>
|
|
518
|
+
);
|
|
519
|
+
})}
|
|
520
|
+
</ol>
|
|
521
|
+
</nav>
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
<!-- Generated by Layout — layout.design -->
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "notion-lite",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"displayName": "Notion",
|
|
5
|
+
"description": "Content-first, block-based design system inspired by Notion",
|
|
6
|
+
"source": "website-extraction",
|
|
7
|
+
"tier": "free",
|
|
8
|
+
"tokenCount": 22,
|
|
9
|
+
"componentCount": 5,
|
|
10
|
+
"aesthetic": "Light, content-first, block-based",
|
|
11
|
+
"layoutUrl": "https://layout.design/kits/notion"
|
|
12
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/* Notion Lite — Design Tokens */
|
|
2
|
+
/* Generated by Layout — layout.design */
|
|
3
|
+
|
|
4
|
+
:root {
|
|
5
|
+
/* Backgrounds */
|
|
6
|
+
--notion-bg-app: #FFFFFF;
|
|
7
|
+
--notion-bg-surface: #F7F6F3;
|
|
8
|
+
--notion-bg-hover: #EEEEE9;
|
|
9
|
+
--notion-bg-selected: #E8E7E3;
|
|
10
|
+
|
|
11
|
+
/* Borders */
|
|
12
|
+
--notion-border: rgba(55, 53, 47, 0.09);
|
|
13
|
+
--notion-border-strong: rgba(55, 53, 47, 0.18);
|
|
14
|
+
|
|
15
|
+
/* Text */
|
|
16
|
+
--notion-text-primary: #37352F;
|
|
17
|
+
--notion-text-secondary: #787774;
|
|
18
|
+
--notion-text-muted: #9B9A97;
|
|
19
|
+
--notion-text-placeholder: rgba(55, 53, 47, 0.4);
|
|
20
|
+
|
|
21
|
+
/* Accent */
|
|
22
|
+
--notion-accent: #2383E2;
|
|
23
|
+
--notion-accent-hover: #1E78D3;
|
|
24
|
+
--notion-accent-subtle: rgba(35, 131, 226, 0.1);
|
|
25
|
+
|
|
26
|
+
/* Callout backgrounds */
|
|
27
|
+
--notion-callout-blue: rgba(35, 131, 226, 0.1);
|
|
28
|
+
--notion-callout-yellow: rgba(255, 184, 0, 0.14);
|
|
29
|
+
--notion-callout-green: rgba(15, 123, 108, 0.1);
|
|
30
|
+
--notion-callout-red: rgba(235, 87, 87, 0.1);
|
|
31
|
+
--notion-callout-gray: rgba(55, 53, 47, 0.06);
|
|
32
|
+
|
|
33
|
+
/* Typography */
|
|
34
|
+
--notion-font-sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
35
|
+
--notion-font-serif: Georgia, 'Times New Roman', serif;
|
|
36
|
+
--notion-font-mono: 'SFMono-Regular', Menlo, Consolas, monospace;
|
|
37
|
+
|
|
38
|
+
/* Spacing */
|
|
39
|
+
--notion-space-2xs: 2px;
|
|
40
|
+
--notion-space-xs: 4px;
|
|
41
|
+
--notion-space-sm: 8px;
|
|
42
|
+
--notion-space-md: 16px;
|
|
43
|
+
--notion-space-lg: 24px;
|
|
44
|
+
--notion-space-xl: 32px;
|
|
45
|
+
|
|
46
|
+
/* Radius */
|
|
47
|
+
--notion-radius-sm: 3px;
|
|
48
|
+
--notion-radius-md: 4px;
|
|
49
|
+
--notion-radius-lg: 6px;
|
|
50
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://tr.designtokens.org/format/",
|
|
3
|
+
"color": {
|
|
4
|
+
"bg": {
|
|
5
|
+
"app": { "$type": "color", "$value": "#FFFFFF" },
|
|
6
|
+
"surface": { "$type": "color", "$value": "#F7F6F3" },
|
|
7
|
+
"hover": { "$type": "color", "$value": "#EEEEE9" },
|
|
8
|
+
"selected": { "$type": "color", "$value": "#E8E7E3" }
|
|
9
|
+
},
|
|
10
|
+
"border": {
|
|
11
|
+
"default": { "$type": "color", "$value": "rgba(55, 53, 47, 0.09)" },
|
|
12
|
+
"strong": { "$type": "color", "$value": "rgba(55, 53, 47, 0.18)" }
|
|
13
|
+
},
|
|
14
|
+
"text": {
|
|
15
|
+
"primary": { "$type": "color", "$value": "#37352F" },
|
|
16
|
+
"secondary": { "$type": "color", "$value": "#787774" },
|
|
17
|
+
"muted": { "$type": "color", "$value": "#9B9A97" },
|
|
18
|
+
"placeholder": { "$type": "color", "$value": "rgba(55, 53, 47, 0.4)" }
|
|
19
|
+
},
|
|
20
|
+
"accent": {
|
|
21
|
+
"default": { "$type": "color", "$value": "#2383E2" },
|
|
22
|
+
"hover": { "$type": "color", "$value": "#1E78D3" },
|
|
23
|
+
"subtle": { "$type": "color", "$value": "rgba(35, 131, 226, 0.1)" }
|
|
24
|
+
},
|
|
25
|
+
"callout": {
|
|
26
|
+
"blue": { "$type": "color", "$value": "rgba(35, 131, 226, 0.1)" },
|
|
27
|
+
"yellow": { "$type": "color", "$value": "rgba(255, 184, 0, 0.14)" },
|
|
28
|
+
"green": { "$type": "color", "$value": "rgba(15, 123, 108, 0.1)" },
|
|
29
|
+
"red": { "$type": "color", "$value": "rgba(235, 87, 87, 0.1)" },
|
|
30
|
+
"gray": { "$type": "color", "$value": "rgba(55, 53, 47, 0.06)" }
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"font": {
|
|
34
|
+
"sans": { "$type": "fontFamily", "$value": ["ui-sans-serif", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "sans-serif"] },
|
|
35
|
+
"serif": { "$type": "fontFamily", "$value": ["Georgia", "Times New Roman", "serif"] },
|
|
36
|
+
"mono": { "$type": "fontFamily", "$value": ["SFMono-Regular", "Menlo", "Consolas", "monospace"] }
|
|
37
|
+
},
|
|
38
|
+
"spacing": {
|
|
39
|
+
"2xs": { "$type": "dimension", "$value": "2px" },
|
|
40
|
+
"xs": { "$type": "dimension", "$value": "4px" },
|
|
41
|
+
"sm": { "$type": "dimension", "$value": "8px" },
|
|
42
|
+
"md": { "$type": "dimension", "$value": "16px" },
|
|
43
|
+
"lg": { "$type": "dimension", "$value": "24px" },
|
|
44
|
+
"xl": { "$type": "dimension", "$value": "32px" }
|
|
45
|
+
},
|
|
46
|
+
"borderRadius": {
|
|
47
|
+
"sm": { "$type": "dimension", "$value": "3px" },
|
|
48
|
+
"md": { "$type": "dimension", "$value": "4px" },
|
|
49
|
+
"lg": { "$type": "dimension", "$value": "6px" }
|
|
50
|
+
}
|
|
51
|
+
}
|