@djangocfg/ui-core 2.1.634 → 2.1.635
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/package.json +9 -8
- package/src/components/data/badge/index.tsx +10 -1
- package/src/components/data/table/index.tsx +12 -2
- package/src/components/forms/button/index.tsx +2 -2
- package/src/components/forms/control-size.ts +10 -10
- package/src/components/navigation/context-menu/index.tsx +7 -7
- package/src/components/navigation/dropdown-menu/index.tsx +7 -7
- package/src/components/navigation/menubar/index.tsx +7 -7
- package/src/components/overlay/popover/index.tsx +1 -1
- package/src/components/select/select.tsx +4 -4
- package/src/styles/css/base.css +79 -0
- package/src/styles/css/presets/notion.css +168 -0
- package/src/styles/css/theme/dark.css +23 -23
- package/src/styles/css/theme/light.css +54 -59
- package/src/styles/presets/presets.ts +1 -0
- package/src/styles/presets/types.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.635",
|
|
4
4
|
"description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-components",
|
|
@@ -119,7 +119,8 @@
|
|
|
119
119
|
"./styles/presets/windows": "./src/styles/css/presets/windows.css",
|
|
120
120
|
"./styles/presets/soft": "./src/styles/css/presets/soft.css",
|
|
121
121
|
"./styles/presets/dense": "./src/styles/css/presets/dense.css",
|
|
122
|
-
"./styles/presets/high-contrast": "./src/styles/css/presets/high-contrast.css"
|
|
122
|
+
"./styles/presets/high-contrast": "./src/styles/css/presets/high-contrast.css",
|
|
123
|
+
"./styles/presets/notion": "./src/styles/css/presets/notion.css"
|
|
123
124
|
},
|
|
124
125
|
"files": [
|
|
125
126
|
"dist",
|
|
@@ -138,8 +139,8 @@
|
|
|
138
139
|
"check:docs": "node scripts/check-docs.mjs"
|
|
139
140
|
},
|
|
140
141
|
"peerDependencies": {
|
|
141
|
-
"@djangocfg/api": "^2.1.
|
|
142
|
-
"@djangocfg/i18n": "^2.1.
|
|
142
|
+
"@djangocfg/api": "^2.1.635",
|
|
143
|
+
"@djangocfg/i18n": "^2.1.635",
|
|
143
144
|
"consola": "^3.4.2",
|
|
144
145
|
"lucide-react": "^1.68.0",
|
|
145
146
|
"moment": "^2.30.1",
|
|
@@ -214,10 +215,10 @@
|
|
|
214
215
|
"vaul": "1.1.2"
|
|
215
216
|
},
|
|
216
217
|
"devDependencies": {
|
|
217
|
-
"@djangocfg/api": "^2.1.
|
|
218
|
-
"@djangocfg/eslint-config": "^2.1.
|
|
219
|
-
"@djangocfg/i18n": "^2.1.
|
|
220
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
218
|
+
"@djangocfg/api": "^2.1.635",
|
|
219
|
+
"@djangocfg/eslint-config": "^2.1.635",
|
|
220
|
+
"@djangocfg/i18n": "^2.1.635",
|
|
221
|
+
"@djangocfg/typescript-config": "^2.1.635",
|
|
221
222
|
"@storybook/react-vite": "^10.6.5",
|
|
222
223
|
"@types/node": "^24.13.3",
|
|
223
224
|
"@types/react": "19.3.0",
|
|
@@ -4,7 +4,7 @@ import * as React from 'react';
|
|
|
4
4
|
import { cn } from '../../../lib/utils';
|
|
5
5
|
|
|
6
6
|
const badgeVariants = cva(
|
|
7
|
-
"inline-flex items-center rounded-
|
|
7
|
+
"inline-flex items-center rounded-(--badge-radius) border px-(--badge-px) py-(--badge-py) text-(length:--badge-font-size) font-(weight:--badge-weight) transition-colors focus:outline-none focus:ring-1 focus:ring-ring",
|
|
8
8
|
{
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
@@ -15,6 +15,15 @@ const badgeVariants = cva(
|
|
|
15
15
|
destructive:
|
|
16
16
|
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
|
17
17
|
outline: "text-foreground",
|
|
18
|
+
gray: "border-transparent bg-(--tag-gray-bg) text-(--tag-gray-fg)",
|
|
19
|
+
brown: "border-transparent bg-(--tag-brown-bg) text-(--tag-brown-fg)",
|
|
20
|
+
orange: "border-transparent bg-(--tag-orange-bg) text-(--tag-orange-fg)",
|
|
21
|
+
yellow: "border-transparent bg-(--tag-yellow-bg) text-(--tag-yellow-fg)",
|
|
22
|
+
green: "border-transparent bg-(--tag-green-bg) text-(--tag-green-fg)",
|
|
23
|
+
blue: "border-transparent bg-(--tag-blue-bg) text-(--tag-blue-fg)",
|
|
24
|
+
purple: "border-transparent bg-(--tag-purple-bg) text-(--tag-purple-fg)",
|
|
25
|
+
pink: "border-transparent bg-(--tag-pink-bg) text-(--tag-pink-fg)",
|
|
26
|
+
red: "border-transparent bg-(--tag-red-bg) text-(--tag-red-fg)",
|
|
18
27
|
},
|
|
19
28
|
},
|
|
20
29
|
defaultVariants: {
|
|
@@ -14,15 +14,25 @@ const Table = React.forwardRef<
|
|
|
14
14
|
* double border and a radius that doesn't match the parent's.
|
|
15
15
|
*/
|
|
16
16
|
frame?: boolean
|
|
17
|
+
/**
|
|
18
|
+
* A database grid, as in Notion: no header band, hairline column dividers, 36px rows,
|
|
19
|
+
* regular-weight column labels. Pair with `frame={false}` on a page-level table.
|
|
20
|
+
*/
|
|
21
|
+
grid?: boolean
|
|
17
22
|
}
|
|
18
|
-
>(({ className, containerClassName, frame = true, ...props }, ref) => {
|
|
23
|
+
>(({ className, containerClassName, frame = true, grid = false, ...props }, ref) => {
|
|
19
24
|
// Scroll container. Cap its height via `containerClassName`
|
|
20
25
|
// (e.g. "max-h-80") to get a scrollable body with a sticky header.
|
|
21
26
|
const scroll = (
|
|
22
27
|
<div className={cn("w-full overflow-auto", containerClassName)}>
|
|
23
28
|
<table
|
|
24
29
|
ref={ref}
|
|
25
|
-
className={cn(
|
|
30
|
+
className={cn(
|
|
31
|
+
"w-full caption-bottom text-sm",
|
|
32
|
+
grid &&
|
|
33
|
+
"[&_thead]:bg-transparent [&_th]:h-8 [&_th]:border-r [&_th]:border-divider [&_th]:font-normal [&_td]:h-9 [&_td]:border-r [&_td]:border-divider [&_td]:py-0 [&_th:last-child]:border-r-0 [&_td:last-child]:border-r-0",
|
|
34
|
+
className
|
|
35
|
+
)}
|
|
26
36
|
{...props}
|
|
27
37
|
/>
|
|
28
38
|
</div>
|
|
@@ -11,7 +11,7 @@ import { Link } from '../../navigation/link';
|
|
|
11
11
|
import { buttonSizeClass, CONTROL_SIZE } from '../control-size';
|
|
12
12
|
|
|
13
13
|
const buttonVariants = cva(
|
|
14
|
-
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius)] text-sm font-
|
|
14
|
+
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius)] text-sm font-(weight:--button-weight) transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
15
15
|
{
|
|
16
16
|
variants: {
|
|
17
17
|
variant: {
|
|
@@ -40,7 +40,7 @@ const buttonVariants = cva(
|
|
|
40
40
|
xs: cn(buttonSizeClass('xs'), "rounded-[var(--radius-sm)]"),
|
|
41
41
|
lg: cn(buttonSizeClass('lg'), "rounded-[var(--radius)]"),
|
|
42
42
|
huge: cn(buttonSizeClass('huge'), "rounded-[var(--radius)]"),
|
|
43
|
-
icon: cn(CONTROL_SIZE.default.height, "w-
|
|
43
|
+
icon: cn(CONTROL_SIZE.default.height, "w-(--control-h-default)"),
|
|
44
44
|
},
|
|
45
45
|
// Most variants ship a baked-in `shadow`/`shadow-sm` for a lifted look.
|
|
46
46
|
// `elevated={false}` flattens that (e.g. controls grouped inside a card —
|
|
@@ -22,32 +22,32 @@ import { cn } from '../../lib/utils';
|
|
|
22
22
|
*/
|
|
23
23
|
export const CONTROL_SIZE = {
|
|
24
24
|
xs: {
|
|
25
|
-
height: 'h-
|
|
26
|
-
minHeight: 'min-h-
|
|
25
|
+
height: 'h-(--control-h-xs)',
|
|
26
|
+
minHeight: 'min-h-(--control-h-xs)',
|
|
27
27
|
button: 'px-2 py-1 text-xs',
|
|
28
28
|
field: 'px-2 py-0.5 text-xs',
|
|
29
29
|
},
|
|
30
30
|
sm: {
|
|
31
|
-
height: 'h-
|
|
32
|
-
minHeight: 'min-h-
|
|
31
|
+
height: 'h-(--control-h-sm)',
|
|
32
|
+
minHeight: 'min-h-(--control-h-sm)',
|
|
33
33
|
button: 'px-3 text-sm',
|
|
34
34
|
field: 'px-2.5 py-1 text-sm',
|
|
35
35
|
},
|
|
36
36
|
default: {
|
|
37
|
-
height: 'h-
|
|
38
|
-
minHeight: 'min-h-
|
|
37
|
+
height: 'h-(--control-h-default)',
|
|
38
|
+
minHeight: 'min-h-(--control-h-default)',
|
|
39
39
|
button: 'px-4 py-2',
|
|
40
40
|
field: 'px-3 py-1 text-base md:text-sm',
|
|
41
41
|
},
|
|
42
42
|
lg: {
|
|
43
|
-
height: 'h-
|
|
44
|
-
minHeight: 'min-h-
|
|
43
|
+
height: 'h-(--control-h-lg)',
|
|
44
|
+
minHeight: 'min-h-(--control-h-lg)',
|
|
45
45
|
button: 'px-8',
|
|
46
46
|
field: 'px-4 py-2 text-base',
|
|
47
47
|
},
|
|
48
48
|
huge: {
|
|
49
|
-
height: 'h-
|
|
50
|
-
minHeight: 'min-h-
|
|
49
|
+
height: 'h-(--control-h-huge)',
|
|
50
|
+
minHeight: 'min-h-(--control-h-huge)',
|
|
51
51
|
button: 'px-10 text-lg',
|
|
52
52
|
},
|
|
53
53
|
} as const;
|
|
@@ -28,7 +28,7 @@ const ContextMenuSubTrigger = React.forwardRef<
|
|
|
28
28
|
<ContextMenuPrimitive.SubTrigger
|
|
29
29
|
ref={ref}
|
|
30
30
|
className={cn(
|
|
31
|
-
"flex cursor-default select-none items-center rounded-[var(--radius-sm)] px-
|
|
31
|
+
"flex cursor-default select-none items-center rounded-[var(--radius-sm)] px-(--menu-item-px) py-(--menu-item-py) text-(length:--menu-font-size) outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
32
32
|
inset && "pl-8",
|
|
33
33
|
className
|
|
34
34
|
)}
|
|
@@ -47,7 +47,7 @@ const ContextMenuSubContent = React.forwardRef<
|
|
|
47
47
|
<ContextMenuPrimitive.SubContent
|
|
48
48
|
ref={ref}
|
|
49
49
|
className={cn(
|
|
50
|
-
"z-[1400] min-w-32 overflow-hidden rounded-[var(--radius-popover)] border bg-popover p-
|
|
50
|
+
"z-[1400] min-w-32 overflow-hidden rounded-[var(--radius-popover)] border bg-popover p-(--menu-padding) text-popover-foreground shadow-(--popup-shadow) data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:duration-75 data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-98 motion-reduce:data-[state=open]:animate-none",
|
|
51
51
|
className
|
|
52
52
|
)}
|
|
53
53
|
{...props}
|
|
@@ -63,7 +63,7 @@ const ContextMenuContent = React.forwardRef<
|
|
|
63
63
|
<ContextMenuPrimitive.Content
|
|
64
64
|
ref={ref}
|
|
65
65
|
className={cn(
|
|
66
|
-
"z-[1400] max-h-(--radix-context-menu-content-available-height) min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-[var(--radius-popover)] border bg-popover p-
|
|
66
|
+
"z-[1400] max-h-(--radix-context-menu-content-available-height) min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-[var(--radius-popover)] border bg-popover p-(--menu-padding) text-popover-foreground shadow-(--popup-shadow) data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:duration-75 data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-98 motion-reduce:data-[state=open]:animate-none",
|
|
67
67
|
className
|
|
68
68
|
)}
|
|
69
69
|
{...props}
|
|
@@ -85,7 +85,7 @@ const ContextMenuItem = React.forwardRef<
|
|
|
85
85
|
ref={ref}
|
|
86
86
|
data-variant={variant}
|
|
87
87
|
className={cn(
|
|
88
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] px-
|
|
88
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] px-(--menu-item-px) py-(--menu-item-py) text-(length:--menu-font-size) outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
89
89
|
"[&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground",
|
|
90
90
|
"data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive [&[data-variant=destructive]_svg]:text-destructive",
|
|
91
91
|
inset && "pl-8",
|
|
@@ -103,7 +103,7 @@ const ContextMenuCheckboxItem = React.forwardRef<
|
|
|
103
103
|
<ContextMenuPrimitive.CheckboxItem
|
|
104
104
|
ref={ref}
|
|
105
105
|
className={cn(
|
|
106
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] py-
|
|
106
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] py-(--menu-item-py) pl-8 pr-2 text-(length:--menu-font-size) outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
107
107
|
"[&_svg]:size-4 [&_svg]:shrink-0",
|
|
108
108
|
className
|
|
109
109
|
)}
|
|
@@ -128,7 +128,7 @@ const ContextMenuRadioItem = React.forwardRef<
|
|
|
128
128
|
<ContextMenuPrimitive.RadioItem
|
|
129
129
|
ref={ref}
|
|
130
130
|
className={cn(
|
|
131
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] py-
|
|
131
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] py-(--menu-item-py) pl-8 pr-2 text-(length:--menu-font-size) outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
132
132
|
"[&_svg]:size-4 [&_svg]:shrink-0",
|
|
133
133
|
className
|
|
134
134
|
)}
|
|
@@ -153,7 +153,7 @@ const ContextMenuLabel = React.forwardRef<
|
|
|
153
153
|
<ContextMenuPrimitive.Label
|
|
154
154
|
ref={ref}
|
|
155
155
|
className={cn(
|
|
156
|
-
"px-
|
|
156
|
+
"px-(--menu-item-px) py-(--menu-item-py) text-(length:--menu-label-font-size) font-(weight:--menu-label-weight) text-(color:--menu-label-color) text-foreground",
|
|
157
157
|
inset && "pl-8",
|
|
158
158
|
className
|
|
159
159
|
)}
|
|
@@ -46,7 +46,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
|
|
46
46
|
<DropdownMenuPrimitive.SubTrigger
|
|
47
47
|
ref={ref}
|
|
48
48
|
className={cn(
|
|
49
|
-
"flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] px-
|
|
49
|
+
"flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] px-(--menu-item-px) py-(--menu-item-py) text-(length:--menu-font-size) outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
50
50
|
inset && "pl-8",
|
|
51
51
|
className
|
|
52
52
|
)}
|
|
@@ -71,7 +71,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
|
|
71
71
|
// and clip. `min()` of the space Radix measured to the viewport edge and a
|
|
72
72
|
// comfortable ceiling — a menu that spans the WHOLE screen reads as a page,
|
|
73
73
|
// not a menu, so we stop well short of the viewport even when it would fit.
|
|
74
|
-
"z-[1400] max-h-[min(var(--radix-dropdown-menu-content-available-height),24rem)] min-w-32 overflow-y-auto overflow-x-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-
|
|
74
|
+
"z-[1400] max-h-[min(var(--radix-dropdown-menu-content-available-height),24rem)] min-w-32 overflow-y-auto overflow-x-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-(--menu-padding) text-popover-foreground shadow-(--popup-shadow) data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
75
75
|
className
|
|
76
76
|
)}
|
|
77
77
|
{...props}
|
|
@@ -106,7 +106,7 @@ const DropdownMenuContent = React.forwardRef<
|
|
|
106
106
|
// render behind it (reported: language list clipped under the dialog).
|
|
107
107
|
// So the layer order is: sheet(200) < shell(1000/1001) < dialog(1300) <
|
|
108
108
|
// popover(1400). A popover always belongs on top of its opener.
|
|
109
|
-
"z-[1400] max-h-[min(var(--radix-dropdown-menu-content-available-height),24rem)] min-w-32 overflow-y-auto overflow-x-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-
|
|
109
|
+
"z-[1400] max-h-[min(var(--radix-dropdown-menu-content-available-height),24rem)] min-w-32 overflow-y-auto overflow-x-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-(--menu-padding) text-popover-foreground shadow-(--popup-shadow)",
|
|
110
110
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
111
111
|
className
|
|
112
112
|
)}
|
|
@@ -127,7 +127,7 @@ const DropdownMenuItem = React.forwardRef<
|
|
|
127
127
|
}
|
|
128
128
|
>(({ className, inset, href, variant = "default", children, ...props }, ref) => {
|
|
129
129
|
const classes = cn(
|
|
130
|
-
"relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] px-
|
|
130
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] px-(--menu-item-px) py-(--menu-item-py) text-(length:--menu-font-size) outline-none transition-colors focus:bg-foreground/[0.07] data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
131
131
|
"data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive",
|
|
132
132
|
inset && "pl-8",
|
|
133
133
|
className
|
|
@@ -163,7 +163,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|
|
163
163
|
<DropdownMenuPrimitive.CheckboxItem
|
|
164
164
|
ref={ref}
|
|
165
165
|
className={cn(
|
|
166
|
-
"relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] py-
|
|
166
|
+
"relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] py-(--menu-item-py) pl-8 pr-2 text-(length:--menu-font-size) outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
167
167
|
className
|
|
168
168
|
)}
|
|
169
169
|
checked={checked}
|
|
@@ -187,7 +187,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
|
|
187
187
|
<DropdownMenuPrimitive.RadioItem
|
|
188
188
|
ref={ref}
|
|
189
189
|
className={cn(
|
|
190
|
-
"relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] py-
|
|
190
|
+
"relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] py-(--menu-item-py) pl-8 pr-2 text-(length:--menu-font-size) outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
191
191
|
className
|
|
192
192
|
)}
|
|
193
193
|
{...props}
|
|
@@ -211,7 +211,7 @@ const DropdownMenuLabel = React.forwardRef<
|
|
|
211
211
|
<DropdownMenuPrimitive.Label
|
|
212
212
|
ref={ref}
|
|
213
213
|
className={cn(
|
|
214
|
-
"px-
|
|
214
|
+
"px-(--menu-item-px) py-(--menu-item-py) text-(length:--menu-label-font-size) font-(weight:--menu-label-weight) text-(color:--menu-label-color)",
|
|
215
215
|
inset && "pl-8",
|
|
216
216
|
className
|
|
217
217
|
)}
|
|
@@ -76,7 +76,7 @@ const MenubarSubTrigger = React.forwardRef<
|
|
|
76
76
|
<MenubarPrimitive.SubTrigger
|
|
77
77
|
ref={ref}
|
|
78
78
|
className={cn(
|
|
79
|
-
"flex cursor-default select-none items-center rounded-[var(--radius-sm)] px-
|
|
79
|
+
"flex cursor-default select-none items-center rounded-[var(--radius-sm)] px-(--menu-item-px) py-(--menu-item-py) text-(length:--menu-font-size) outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
80
80
|
inset && "pl-8",
|
|
81
81
|
className
|
|
82
82
|
)}
|
|
@@ -95,7 +95,7 @@ const MenubarSubContent = React.forwardRef<
|
|
|
95
95
|
<MenubarPrimitive.SubContent
|
|
96
96
|
ref={ref}
|
|
97
97
|
className={cn(
|
|
98
|
-
"z-[1400] min-w-32 overflow-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-
|
|
98
|
+
"z-[1400] min-w-32 overflow-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-(--menu-padding) text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
99
99
|
className
|
|
100
100
|
)}
|
|
101
101
|
{...props}
|
|
@@ -118,7 +118,7 @@ const MenubarContent = React.forwardRef<
|
|
|
118
118
|
alignOffset={alignOffset}
|
|
119
119
|
sideOffset={sideOffset}
|
|
120
120
|
className={cn(
|
|
121
|
-
"z-[1400] min-w-48 overflow-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-
|
|
121
|
+
"z-[1400] min-w-48 overflow-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-(--menu-padding) text-popover-foreground shadow-(--popup-shadow) data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
122
122
|
className
|
|
123
123
|
)}
|
|
124
124
|
{...props}
|
|
@@ -139,7 +139,7 @@ const MenubarItem = React.forwardRef<
|
|
|
139
139
|
}
|
|
140
140
|
>(({ className, inset, href, variant = "default", children, ...props }, ref) => {
|
|
141
141
|
const classes = cn(
|
|
142
|
-
"relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] px-
|
|
142
|
+
"relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] px-(--menu-item-px) py-(--menu-item-py) text-(length:--menu-font-size) outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
143
143
|
"data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive",
|
|
144
144
|
inset && "pl-8",
|
|
145
145
|
className
|
|
@@ -175,7 +175,7 @@ const MenubarCheckboxItem = React.forwardRef<
|
|
|
175
175
|
<MenubarPrimitive.CheckboxItem
|
|
176
176
|
ref={ref}
|
|
177
177
|
className={cn(
|
|
178
|
-
"relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] py-
|
|
178
|
+
"relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] py-(--menu-item-py) pl-8 pr-2 text-(length:--menu-font-size) outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
179
179
|
className
|
|
180
180
|
)}
|
|
181
181
|
checked={checked}
|
|
@@ -198,7 +198,7 @@ const MenubarRadioItem = React.forwardRef<
|
|
|
198
198
|
<MenubarPrimitive.RadioItem
|
|
199
199
|
ref={ref}
|
|
200
200
|
className={cn(
|
|
201
|
-
"relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] py-
|
|
201
|
+
"relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] py-(--menu-item-py) pl-8 pr-2 text-(length:--menu-font-size) outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
202
202
|
className
|
|
203
203
|
)}
|
|
204
204
|
{...props}
|
|
@@ -222,7 +222,7 @@ const MenubarLabel = React.forwardRef<
|
|
|
222
222
|
<MenubarPrimitive.Label
|
|
223
223
|
ref={ref}
|
|
224
224
|
className={cn(
|
|
225
|
-
"px-
|
|
225
|
+
"px-(--menu-item-px) py-(--menu-item-py) text-(length:--menu-label-font-size) font-(weight:--menu-label-weight) text-(color:--menu-label-color)",
|
|
226
226
|
inset && "pl-8",
|
|
227
227
|
className
|
|
228
228
|
)}
|
|
@@ -57,7 +57,7 @@ const PopoverContent = React.forwardRef<
|
|
|
57
57
|
// (in the class list below) caps the whole panel to.
|
|
58
58
|
collisionPadding={collisionPadding}
|
|
59
59
|
className={cn(
|
|
60
|
-
"popover-panel z-[1400] w-72 rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-4 text-popover-foreground shadow-
|
|
60
|
+
"popover-panel z-[1400] w-72 rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-4 text-popover-foreground shadow-(--popup-shadow) outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
61
61
|
className
|
|
62
62
|
)}
|
|
63
63
|
{...props}
|
|
@@ -147,7 +147,7 @@ const SelectContent = React.forwardRef<
|
|
|
147
147
|
<SelectPrimitive.Content
|
|
148
148
|
ref={ref}
|
|
149
149
|
className={cn(
|
|
150
|
-
"relative z-[1400] max-h-(--radix-select-content-available-height) min-w-32 overflow-y-auto overflow-x-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl text-popover-foreground shadow-
|
|
150
|
+
"relative z-[1400] max-h-(--radix-select-content-available-height) min-w-32 overflow-y-auto overflow-x-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl text-popover-foreground shadow-(--popup-shadow) data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
151
151
|
position === "popper" &&
|
|
152
152
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
153
153
|
className
|
|
@@ -158,7 +158,7 @@ const SelectContent = React.forwardRef<
|
|
|
158
158
|
<SelectScrollUpButton />
|
|
159
159
|
<SelectPrimitive.Viewport
|
|
160
160
|
className={cn(
|
|
161
|
-
"p-
|
|
161
|
+
"p-(--menu-padding)",
|
|
162
162
|
position === "popper" &&
|
|
163
163
|
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
164
164
|
)}
|
|
@@ -177,7 +177,7 @@ const SelectLabel = React.forwardRef<
|
|
|
177
177
|
>(({ className, ...props }, ref) => (
|
|
178
178
|
<SelectPrimitive.Label
|
|
179
179
|
ref={ref}
|
|
180
|
-
className={cn("px-
|
|
180
|
+
className={cn("px-(--menu-item-px) py-(--menu-item-py) text-(length:--menu-label-font-size) font-(weight:--menu-label-weight) text-(color:--menu-label-color)", className)}
|
|
181
181
|
{...props}
|
|
182
182
|
/>
|
|
183
183
|
))
|
|
@@ -202,7 +202,7 @@ const SelectItem = React.forwardRef<
|
|
|
202
202
|
ref={ref}
|
|
203
203
|
value={value === '' ? EMPTY_SENTINEL : value}
|
|
204
204
|
className={cn(
|
|
205
|
-
"relative flex w-full cursor-default select-none items-center rounded-[var(--radius-sm)] py-
|
|
205
|
+
"relative flex w-full cursor-default select-none items-center rounded-[var(--radius-sm)] py-(--menu-item-py) pl-2 pr-8 text-(length:--menu-font-size) outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
206
206
|
className
|
|
207
207
|
)}
|
|
208
208
|
{...props}
|
package/src/styles/css/base.css
CHANGED
|
@@ -60,6 +60,63 @@
|
|
|
60
60
|
--font-size-xl: 1.125rem;
|
|
61
61
|
--line-height-base: 1.5;
|
|
62
62
|
--letter-spacing-base: 0em;
|
|
63
|
+
|
|
64
|
+
/* Menu tokens — one density for dropdown, context menu, menubar and select panels.
|
|
65
|
+
* Components read these; a preset changes them in one place (never per-component literals). */
|
|
66
|
+
/* Chat prose follows the theme scale (Notion: 14px / 1.5). widget-chat reads these two. */
|
|
67
|
+
/* Notion-style tag colours: a tinted fill with matching ink (Badge variants gray…red). */
|
|
68
|
+
--tag-gray-bg: #e3e2e0;
|
|
69
|
+
--tag-gray-fg: #32302c;
|
|
70
|
+
--tag-brown-bg: #eee0da;
|
|
71
|
+
--tag-brown-fg: #442a1e;
|
|
72
|
+
--tag-orange-bg: #fadec9;
|
|
73
|
+
--tag-orange-fg: #49290e;
|
|
74
|
+
--tag-yellow-bg: #fdecc8;
|
|
75
|
+
--tag-yellow-fg: #402c1b;
|
|
76
|
+
--tag-green-bg: #dbeddb;
|
|
77
|
+
--tag-green-fg: #1c3829;
|
|
78
|
+
--tag-blue-bg: #d3e5ef;
|
|
79
|
+
--tag-blue-fg: #183347;
|
|
80
|
+
--tag-purple-bg: #e8deee;
|
|
81
|
+
--tag-purple-fg: #412454;
|
|
82
|
+
--tag-pink-bg: #f5e0e9;
|
|
83
|
+
--tag-pink-fg: #4c2337;
|
|
84
|
+
--tag-red-bg: #ffe2dd;
|
|
85
|
+
--tag-red-fg: #5d1715;
|
|
86
|
+
/* The control height ladder every Button, Input and Select reads (control-size.ts). */
|
|
87
|
+
--control-h-xs: 1.75rem;
|
|
88
|
+
--control-h-sm: 2rem;
|
|
89
|
+
--control-h-default: 2.25rem;
|
|
90
|
+
--control-h-lg: 2.75rem;
|
|
91
|
+
--control-h-huge: 3.5rem;
|
|
92
|
+
--button-weight: 600;
|
|
93
|
+
/* PrivateLayout sidebar rows (desktop). */
|
|
94
|
+
--sidebar-item-h: 2.25rem;
|
|
95
|
+
--sidebar-item-font: var(--font-size-sm);
|
|
96
|
+
--sidebar-item-radius: 0.5rem;
|
|
97
|
+
--sidebar-divider-h: 1px;
|
|
98
|
+
/* Badge metrics. Defaults keep the compact label; a preset widens them to a 20px tag. */
|
|
99
|
+
--badge-radius: var(--radius);
|
|
100
|
+
--badge-px: 0.625rem;
|
|
101
|
+
--badge-py: 0.125rem;
|
|
102
|
+
--badge-font-size: 0.75rem;
|
|
103
|
+
--badge-weight: 600;
|
|
104
|
+
--chat-text: var(--font-size-base);
|
|
105
|
+
--chat-leading: 1.5;
|
|
106
|
+
/* Side-panel title (widget-chat header): small caps by default, plain sentence case in Notion. */
|
|
107
|
+
--panel-title-weight: 600;
|
|
108
|
+
--panel-title-tracking: 0.025em;
|
|
109
|
+
--panel-title-color: var(--muted-foreground);
|
|
110
|
+
--panel-title-transform: uppercase;
|
|
111
|
+
/* Shadow of every popup surface (menus, select, popover). */
|
|
112
|
+
--popup-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
113
|
+
--menu-font-size: var(--font-size-sm);
|
|
114
|
+
--menu-label-font-size: var(--menu-font-size);
|
|
115
|
+
--menu-label-weight: 600;
|
|
116
|
+
--menu-label-color: inherit;
|
|
117
|
+
--menu-item-px: 0.5rem;
|
|
118
|
+
--menu-item-py: 0.375rem;
|
|
119
|
+
--menu-padding: 0.25rem;
|
|
63
120
|
}
|
|
64
121
|
|
|
65
122
|
/* Base element styles */
|
|
@@ -200,3 +257,25 @@ body {
|
|
|
200
257
|
-webkit-font-smoothing: antialiased;
|
|
201
258
|
-moz-osx-font-smoothing: grayscale;
|
|
202
259
|
}
|
|
260
|
+
|
|
261
|
+
/* Tag colours on a dark page: translucent tints, near-white ink. */
|
|
262
|
+
.dark {
|
|
263
|
+
--tag-gray-bg: rgba(255,252,235,.306);
|
|
264
|
+
--tag-gray-fg: #f0efed;
|
|
265
|
+
--tag-brown-bg: rgba(210,160,120,.40);
|
|
266
|
+
--tag-brown-fg: #f0e6df;
|
|
267
|
+
--tag-orange-bg: rgba(255,146,60,.45);
|
|
268
|
+
--tag-orange-fg: #fbeadb;
|
|
269
|
+
--tag-yellow-bg: rgba(255,188,53,.46);
|
|
270
|
+
--tag-yellow-fg: #f9f3dc;
|
|
271
|
+
--tag-green-bg: rgba(113,255,175,.337);
|
|
272
|
+
--tag-green-fg: #e8f1ec;
|
|
273
|
+
--tag-blue-bg: rgba(81,166,255,.494);
|
|
274
|
+
--tag-blue-fg: #e5f2fc;
|
|
275
|
+
--tag-purple-bg: rgba(208,147,255,.427);
|
|
276
|
+
--tag-purple-fg: #f3ebf9;
|
|
277
|
+
--tag-pink-bg: rgba(255,133,192,.427);
|
|
278
|
+
--tag-pink-fg: #faE9f1;
|
|
279
|
+
--tag-red-bg: rgba(255,116,105,.525);
|
|
280
|
+
--tag-red-fg: #fce9e7;
|
|
281
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/* Authoritative static preset: notion.
|
|
2
|
+
*
|
|
3
|
+
* Measured from app.notion.com on 2026-11-06 (its `:root, .notion-light-theme`
|
|
4
|
+
* custom properties), not eyeballed from a screenshot:
|
|
5
|
+
* canvas #fff · rail #f9f8f7 · hover #f0efed · hairline #e6e5e3
|
|
6
|
+
* ink #2c2c2b · secondary ink #7d7a75 · accent blue #2783de
|
|
7
|
+
* Notion is neutral: no brand colour on surfaces, one blue for the single
|
|
8
|
+
* primary action, and a warm-grey hover. Everything else is ink on white.
|
|
9
|
+
*/
|
|
10
|
+
:root {
|
|
11
|
+
--background: hsl(0 0% 100%);
|
|
12
|
+
--foreground: hsl(60 1% 17%);
|
|
13
|
+
--card: hsl(0 0% 100%);
|
|
14
|
+
--card-foreground: hsl(60 1% 17%);
|
|
15
|
+
--popover: hsl(0 0% 100%);
|
|
16
|
+
--popover-foreground: hsl(60 1% 17%);
|
|
17
|
+
/* Notion's blue is #2783de (white on it: 3.9:1). One lightness step deeper
|
|
18
|
+
keeps white button labels at AA (4.6:1) while staying the same hue. */
|
|
19
|
+
--primary: hsl(209 74% 47%);
|
|
20
|
+
--primary-foreground: hsl(0 0% 100%);
|
|
21
|
+
/* #f9f8f7 — Notion's "secondary background". */
|
|
22
|
+
--secondary: hsl(30 14% 97%);
|
|
23
|
+
--secondary-foreground: hsl(60 1% 17%);
|
|
24
|
+
/* #f0efed. 6.5 points under the white page: a visible chip, not a plate. */
|
|
25
|
+
--muted: hsl(40 9% 93.5%);
|
|
26
|
+
/* #7d7a75 darkened one step: AA on white and on the muted fill. */
|
|
27
|
+
--muted-foreground: hsl(37 3% 43%);
|
|
28
|
+
--surface-hover: hsl(40 9% 95%);
|
|
29
|
+
/* Hover on the page: the same warm grey as Notion's row hover. */
|
|
30
|
+
--accent: hsl(40 9% 93.5%);
|
|
31
|
+
--accent-foreground: hsl(60 1% 17%);
|
|
32
|
+
/* #cf5148 text red, #e56458 fill red; deeper for white labels. */
|
|
33
|
+
--destructive: hsl(5 62% 48%);
|
|
34
|
+
--destructive-foreground: hsl(0 0% 100%);
|
|
35
|
+
--on-destructive: hsl(0 0% 100%);
|
|
36
|
+
/* #e6e5e3 */
|
|
37
|
+
--border: hsl(40 6% 90%);
|
|
38
|
+
/* Notion fields are white with a hairline, not a grey plate. */
|
|
39
|
+
--input: hsl(0 0% 100%);
|
|
40
|
+
--divider: hsl(40 6% 92%);
|
|
41
|
+
--ring: hsl(209 74% 52%);
|
|
42
|
+
--overlay: hsl(0 0% 0% / 0.4);
|
|
43
|
+
--radius: 0.375rem;
|
|
44
|
+
--shadow-card: 0 0 0 1px rgb(15 15 15 / 0.05), 0 3px 6px rgb(15 15 15 / 0.1);
|
|
45
|
+
/* Notion's sidebar is #f9f8f7 under #fff content; its hover is a ~8% ink
|
|
46
|
+
veil, 5 points down. */
|
|
47
|
+
--sidebar-background: hsl(30 14% 97%);
|
|
48
|
+
--sidebar-foreground: hsl(60 1% 17%);
|
|
49
|
+
--sidebar-primary: hsl(209 74% 47%);
|
|
50
|
+
--sidebar-primary-foreground: hsl(0 0% 100%);
|
|
51
|
+
--sidebar-accent: hsl(40 7% 92%);
|
|
52
|
+
--sidebar-accent-foreground: hsl(60 1% 17%);
|
|
53
|
+
--sidebar-border: hsl(40 6% 90%);
|
|
54
|
+
--sidebar-ring: hsl(209 74% 52%);
|
|
55
|
+
--code: hsl(30 14% 97%);
|
|
56
|
+
--code-foreground: hsl(60 1% 17%);
|
|
57
|
+
--code-border: hsl(40 6% 90%);
|
|
58
|
+
--code-inline: hsl(40 9% 93.5%);
|
|
59
|
+
--code-inline-foreground: hsl(5 62% 42%);
|
|
60
|
+
--chart-1: hsl(209 74% 52%);
|
|
61
|
+
--chart-2: hsl(147 30% 47%);
|
|
62
|
+
--chart-3: hsl(277 30% 55%);
|
|
63
|
+
--chart-4: hsl(31 65% 51%);
|
|
64
|
+
--chart-5: hsl(5 62% 59%);
|
|
65
|
+
/* Status tints are Notion's *BacPri / *TexPri / *BorPri triples. */
|
|
66
|
+
--warning: hsl(39 60% 51%);
|
|
67
|
+
--warning-background: hsl(52 70% 96%);
|
|
68
|
+
--warning-foreground: hsl(45 53% 26%);
|
|
69
|
+
--warning-border: hsl(46 66% 82%);
|
|
70
|
+
--success: hsl(147 30% 47%);
|
|
71
|
+
--success-background: hsl(147 20% 96%);
|
|
72
|
+
--success-foreground: hsl(147 33% 25%);
|
|
73
|
+
--success-border: hsl(147 22% 87%);
|
|
74
|
+
--destructive-background: hsl(0 55% 97%);
|
|
75
|
+
--destructive-border: hsl(5 70% 92%);
|
|
76
|
+
--info: hsl(209 74% 52%);
|
|
77
|
+
--info-background: hsl(209 80% 97%);
|
|
78
|
+
--info-foreground: hsl(209 50% 29%);
|
|
79
|
+
--info-border: hsl(209 75% 91%);
|
|
80
|
+
/* Notion types in the system UI stack, 14px, with a 1.5 leading. */
|
|
81
|
+
--font-sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI Variable Display", "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif;
|
|
82
|
+
--font-size-base: 0.875rem;
|
|
83
|
+
--font-size-sm: 0.8125rem;
|
|
84
|
+
--font-size-xs: 0.75rem;
|
|
85
|
+
--line-height-base: 1.5;
|
|
86
|
+
/* Notion's menus: 14px type, 28px rows (4px vertical padding), 4px panel padding. */
|
|
87
|
+
/* Notion AI: messages 14px / 20px. */
|
|
88
|
+
/* Notion's controls are flat: the hairline does the work, no drop shadow. */
|
|
89
|
+
--shadow: 0 0 #0000;
|
|
90
|
+
--shadow-xs: 0 0 #0000;
|
|
91
|
+
--shadow-sm: 0 0 #0000;
|
|
92
|
+
/* Notion's controls are 24/28/32px, 14px type at weight 500. */
|
|
93
|
+
--control-h-xs: 1.5rem;
|
|
94
|
+
--control-h-sm: 1.75rem;
|
|
95
|
+
--control-h-default: 2rem;
|
|
96
|
+
--control-h-lg: 2.5rem;
|
|
97
|
+
--button-weight: 500;
|
|
98
|
+
/* Notion's sidebar row: 30px, 14px type, 6px corner. */
|
|
99
|
+
--sidebar-item-h: 1.875rem;
|
|
100
|
+
--sidebar-item-font: var(--font-size-base);
|
|
101
|
+
--sidebar-item-radius: 0.375rem;
|
|
102
|
+
/* Notion separates sidebar groups by space, not by a rule. */
|
|
103
|
+
--sidebar-divider-h: 0px;
|
|
104
|
+
/* Notion's top bar is 44px. */
|
|
105
|
+
--panel-header-h: 2.75rem;
|
|
106
|
+
/* Notion tags: 20px, 4px corner, 14px regular. */
|
|
107
|
+
--badge-radius: 0.25rem;
|
|
108
|
+
--badge-px: 0.375rem;
|
|
109
|
+
--badge-py: 0;
|
|
110
|
+
--badge-font-size: 0.875rem;
|
|
111
|
+
--badge-weight: 400;
|
|
112
|
+
--chat-text: var(--font-size-base);
|
|
113
|
+
--chat-leading: 1.5;
|
|
114
|
+
/* The AI composer is a raised 1px-ringed card (#202020 on #191919 in dark). */
|
|
115
|
+
--chat-composer-bg: var(--card);
|
|
116
|
+
--chat-composer-radius: 0.75rem;
|
|
117
|
+
--chat-composer-button-radius: 9999px;
|
|
118
|
+
--chat-composer-ring-w: 1px;
|
|
119
|
+
--chat-composer-ring: var(--border);
|
|
120
|
+
--chat-composer-ring-focus: var(--ring);
|
|
121
|
+
--panel-title-weight: 500;
|
|
122
|
+
--panel-title-tracking: 0;
|
|
123
|
+
--panel-title-color: var(--foreground);
|
|
124
|
+
--panel-title-transform: none;
|
|
125
|
+
--menu-font-size: var(--font-size-base);
|
|
126
|
+
/* Menu section labels: 12px / 500 in the muted ink. */
|
|
127
|
+
--menu-label-font-size: var(--font-size-xs);
|
|
128
|
+
--menu-label-weight: 500;
|
|
129
|
+
--menu-label-color: var(--muted-foreground);
|
|
130
|
+
--radius-popover: 0.625rem;
|
|
131
|
+
--popup-shadow: 0 0 0 1px rgb(15 15 15 / 0.05), 0 3px 6px rgb(15 15 15 / 0.1), 0 9px 24px rgb(15 15 15 / 0.2);
|
|
132
|
+
--menu-item-px: 0.5rem;
|
|
133
|
+
--menu-item-py: 0.25rem;
|
|
134
|
+
--menu-padding: 0.25rem;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Notion dark (#191919 page, #202020 rail, #2f2f2f hairline). */
|
|
138
|
+
.dark {
|
|
139
|
+
--background: hsl(0 0% 10%);
|
|
140
|
+
--foreground: hsl(40 9% 93.5%);
|
|
141
|
+
--card: hsl(0 0% 12%);
|
|
142
|
+
--card-foreground: hsl(40 9% 93.5%);
|
|
143
|
+
--popover: hsl(0 0% 15%);
|
|
144
|
+
--popover-foreground: hsl(40 9% 93.5%);
|
|
145
|
+
--primary: hsl(209 74% 52%);
|
|
146
|
+
--primary-foreground: hsl(0 0% 100%);
|
|
147
|
+
--secondary: hsl(0 0% 15%);
|
|
148
|
+
--secondary-foreground: hsl(0 0% 81%);
|
|
149
|
+
--muted: hsl(0 0% 15%);
|
|
150
|
+
--muted-foreground: hsl(0 0% 60%);
|
|
151
|
+
--accent: hsl(0 0% 17%);
|
|
152
|
+
--accent-foreground: hsl(0 0% 90%);
|
|
153
|
+
--border: hsl(0 0% 18%);
|
|
154
|
+
--input: hsl(0 0% 18%);
|
|
155
|
+
--divider: hsl(0 0% 15%);
|
|
156
|
+
--ring: hsl(209 74% 52%);
|
|
157
|
+
--sidebar-background: hsl(0 0% 12.5%);
|
|
158
|
+
--sidebar-foreground: hsl(0 0% 81%);
|
|
159
|
+
--sidebar-primary: hsl(209 74% 52%);
|
|
160
|
+
--sidebar-primary-foreground: hsl(0 0% 100%);
|
|
161
|
+
--sidebar-accent: hsl(0 0% 17%);
|
|
162
|
+
--sidebar-accent-foreground: hsl(0 0% 90%);
|
|
163
|
+
--sidebar-border: hsl(0 0% 18%);
|
|
164
|
+
--sidebar-ring: hsl(209 74% 52%);
|
|
165
|
+
--chart-1: hsl(209 74% 58%);
|
|
166
|
+
/* Dark popups: a 1px #383836 ring plus a soft drop. */
|
|
167
|
+
--popup-shadow: 0 0 0 1px hsl(60 2% 22%), 0 14px 28px -6px rgb(0 0 0 / 0.2), 0 2px 4px -1px rgb(0 0 0 / 0.12);
|
|
168
|
+
}
|
|
@@ -19,21 +19,21 @@
|
|
|
19
19
|
* panels and menus read as raised. muted recesses below the page.
|
|
20
20
|
* page ≈ bg-100 card ≈ bg-000(18.4%) popover 18.8% (claude.ai menus) */
|
|
21
21
|
--background: hsl(0 0% 10%);
|
|
22
|
-
--foreground: hsl(0 0%
|
|
23
|
-
--card: hsl(0 0%
|
|
24
|
-
--card-foreground: hsl(0 0%
|
|
25
|
-
--popover: hsl(0 0%
|
|
26
|
-
--popover-foreground: hsl(0 0%
|
|
22
|
+
--foreground: hsl(0 0% 81%);
|
|
23
|
+
--card: hsl(0 0% 12%);
|
|
24
|
+
--card-foreground: hsl(0 0% 81%);
|
|
25
|
+
--popover: hsl(0 0% 15%);
|
|
26
|
+
--popover-foreground: hsl(0 0% 81%);
|
|
27
27
|
/* Brand clay #d97757 (Claude). Bright enough on dark chrome to carry the full swatch. */
|
|
28
|
-
--primary: hsl(
|
|
28
|
+
--primary: hsl(209 74% 52%);
|
|
29
29
|
/* Dark label on clay — 5.8:1, stronger than white-on-clay (3.1:1) */
|
|
30
|
-
--primary-foreground: hsl(0 0%
|
|
30
|
+
--primary-foreground: hsl(0 0% 100%);
|
|
31
31
|
/* Secondary: elevated warm-gray control surface + light label */
|
|
32
|
-
--secondary: hsl(0 0%
|
|
33
|
-
--secondary-foreground: hsl(0 0%
|
|
32
|
+
--secondary: hsl(0 0% 15%);
|
|
33
|
+
--secondary-foreground: hsl(0 0% 81%);
|
|
34
34
|
/* Recessed surface — below the page (Claude bg-200), for input rest / chips. */
|
|
35
|
-
--muted: hsl(0 0%
|
|
36
|
-
--muted-foreground: hsl(0 0%
|
|
35
|
+
--muted: hsl(0 0% 15%);
|
|
36
|
+
--muted-foreground: hsl(0 0% 60%);
|
|
37
37
|
/* Neutral interaction surface: a quiet lift above the current dark page. */
|
|
38
38
|
--surface-hover: color-mix(in oklab, var(--card) 96%, var(--foreground));
|
|
39
39
|
/* The counterpart to --surface-hover: a surface that reads as sunk BELOW the
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
* and composites over whatever surface the band happens to sit on. */
|
|
50
50
|
--surface-recessed: rgb(0 0 0 / 0.35);
|
|
51
51
|
/* Hover/active surface (rails, menus, tabs) — a quiet warm lift above the page. */
|
|
52
|
-
--accent: hsl(0 0%
|
|
53
|
-
--accent-foreground: hsl(0 0%
|
|
52
|
+
--accent: hsl(0 0% 17%);
|
|
53
|
+
--accent-foreground: hsl(0 0% 90%);
|
|
54
54
|
--destructive: hsl(0 67% 60%);
|
|
55
55
|
--destructive-foreground: hsl(0 0% 98%);
|
|
56
56
|
/* Soft warm border — Claude draws hairlines from a light border at low
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
Capped by MAXIMUM_BORDER_GAP in check-preset-contrast.mjs. */
|
|
62
62
|
--border: hsl(0 0% 18%);
|
|
63
63
|
/* Input surface — a notch above card so fields read as raised controls. */
|
|
64
|
-
--input: hsl(0 0%
|
|
64
|
+
--input: hsl(0 0% 18%);
|
|
65
65
|
/* Divider — TRANSLUCENT hairline (Apple-style): a light warm-gray at low alpha
|
|
66
66
|
* that dissolves into whatever sits behind it, so structural separators
|
|
67
67
|
* (columns, headers, rows) stay quiet on the dark page instead of reading as a
|
|
@@ -72,12 +72,12 @@
|
|
|
72
72
|
left of "кондово" after the fills were calmed. Over a 12% card this
|
|
73
73
|
lands at 15.4% (step 3.4) against the border's 18% (step 6.0): a clear
|
|
74
74
|
hierarchy, frame first. */
|
|
75
|
-
--divider: hsl(0 0%
|
|
75
|
+
--divider: hsl(0 0% 15%);
|
|
76
76
|
/* Overlay — modal scrim / backdrop behind dialogs, drawers, sheets. Black in
|
|
77
77
|
* both themes; slightly darker here so it still reads on the dark page. */
|
|
78
78
|
--overlay: hsl(0 0% 0% / 0.7);
|
|
79
79
|
/* Focus ring — blue (system-accent feel), independent of the cyan brand. */
|
|
80
|
-
--ring: hsl(
|
|
80
|
+
--ring: hsl(209 74% 52%);
|
|
81
81
|
--shadow-card: none;
|
|
82
82
|
|
|
83
83
|
/* Code surface — see light.css for the rationale. On dark we want
|
|
@@ -94,13 +94,13 @@
|
|
|
94
94
|
* block; rail and page share one tone). A hair darker than --background for
|
|
95
95
|
* subtle separation, not a hard black slab. */
|
|
96
96
|
--sidebar-background: hsl(0 0% 12.5%);
|
|
97
|
-
--sidebar-foreground: hsl(0 0%
|
|
98
|
-
--sidebar-primary: hsl(
|
|
99
|
-
--sidebar-primary-foreground: hsl(0 0%
|
|
100
|
-
--sidebar-accent: hsl(0 0%
|
|
101
|
-
--sidebar-accent-foreground: hsl(0 0%
|
|
97
|
+
--sidebar-foreground: hsl(0 0% 81%);
|
|
98
|
+
--sidebar-primary: hsl(209 74% 52%);
|
|
99
|
+
--sidebar-primary-foreground: hsl(0 0% 100%);
|
|
100
|
+
--sidebar-accent: hsl(0 0% 17%);
|
|
101
|
+
--sidebar-accent-foreground: hsl(0 0% 90%);
|
|
102
102
|
--sidebar-border: hsl(0 0% 18%);
|
|
103
|
-
--sidebar-ring: hsl(
|
|
103
|
+
--sidebar-ring: hsl(209 74% 52%);
|
|
104
104
|
/* ── Status surfaces (dark) ──────────────────────────────────────
|
|
105
105
|
* Dark variants: dim backgrounds (~8-12% lightness) so banners
|
|
106
106
|
* don't blow out against the near-black page. Foreground raised
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
/* Chart colors — chart-1 aligned with brand.
|
|
149
149
|
* Fully-wrapped hsl() like every other token (consume via `bg-chart-N` or
|
|
150
150
|
* `var(--chart-N)`, never `hsl(var(--chart-N))`). */
|
|
151
|
-
--chart-1: hsl(
|
|
151
|
+
--chart-1: hsl(209 74% 58%);
|
|
152
152
|
--chart-2: hsl(142 76% 36%);
|
|
153
153
|
--chart-3: hsl(262 83% 58%);
|
|
154
154
|
--chart-4: hsl(26 90% 57%);
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Light Theme Colors
|
|
2
|
+
* Light Theme Colors — Notion-neutral (retuned 2026-11-06 from app.notion.com's own custom properties).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* for buttons, links, and `text-primary`. The full-brightness clay is reserved for brand marks / dark
|
|
7
|
-
* UI; the dark theme carries the brighter swatch via `.dark`.
|
|
8
|
-
*
|
|
9
|
-
* Neutrals follow Claude's current theme: a faintly WARM off-white canvas (hue ~60, not pure gray) so
|
|
10
|
-
* cards and chips read against the page the way they do on claude.ai.
|
|
4
|
+
* White canvas, #f9f8f7 rail, warm-grey hover, one blue for the primary action. The previous Claude "clay"
|
|
5
|
+
* brand orange is gone from surfaces; the `notion` preset is a pinned copy of these values for side-by-side checks.
|
|
11
6
|
*
|
|
12
7
|
* Pairs `--primary` / `--primary-foreground` are tuned together. Prefer editing tokens over ad hoc classes.
|
|
13
8
|
*/
|
|
@@ -17,23 +12,23 @@
|
|
|
17
12
|
* fills and a white raised card. Keep `muted` below `background` in light mode
|
|
18
13
|
* so chips, message bubbles, and secondary controls never dissolve into the
|
|
19
14
|
* page. */
|
|
20
|
-
--background: hsl(
|
|
21
|
-
--foreground: hsl(
|
|
15
|
+
--background: hsl(0 0% 100%);
|
|
16
|
+
--foreground: hsl(60 1% 17%);
|
|
22
17
|
--card: hsl(0 0% 100%);
|
|
23
|
-
--card-foreground: hsl(
|
|
18
|
+
--card-foreground: hsl(60 1% 17%);
|
|
24
19
|
--popover: hsl(0 0% 100%);
|
|
25
|
-
--popover-foreground: hsl(
|
|
20
|
+
--popover-foreground: hsl(60 1% 17%);
|
|
26
21
|
/* Interactive primary on white — deep clay (~#b6543a): readable white-on-fill (5.0:1) + calmer than the brand swatch */
|
|
27
|
-
--primary: hsl(
|
|
22
|
+
--primary: hsl(209 74% 47%);
|
|
28
23
|
--primary-foreground: hsl(0 0% 100%);
|
|
29
|
-
--secondary: hsl(
|
|
30
|
-
--secondary-foreground: hsl(60
|
|
31
|
-
--muted: hsl(
|
|
32
|
-
--muted-foreground: hsl(
|
|
24
|
+
--secondary: hsl(30 14% 97%);
|
|
25
|
+
--secondary-foreground: hsl(60 1% 17%);
|
|
26
|
+
--muted: hsl(40 9% 93.5%);
|
|
27
|
+
--muted-foreground: hsl(37 3% 43%);
|
|
33
28
|
/* Neutral interaction surface: a quiet step below the current light canvas.
|
|
34
29
|
* It deliberately keys off --background, not white --card, so hover remains
|
|
35
30
|
* visible on translucent menus and on plain pages alike. */
|
|
36
|
-
--surface-hover:
|
|
31
|
+
--surface-hover: hsl(40 9% 95%);
|
|
37
32
|
/* The counterpart to --surface-hover: a surface that reads as sunk BELOW the
|
|
38
33
|
* page. A translucent black for the same reason as the dark theme's, but much
|
|
39
34
|
* weaker: the same overlay costs far more contrast on a near-white ground,
|
|
@@ -41,23 +36,23 @@
|
|
|
41
36
|
--surface-recessed: rgb(0 0 0 / 0.045);
|
|
42
37
|
/* Neutral hover/active surface — quiet warm-gray lift (Claude bg-200), a
|
|
43
38
|
* clear step (≥4L) below the 97% canvas. */
|
|
44
|
-
--accent: hsl(
|
|
45
|
-
--accent-foreground: hsl(
|
|
39
|
+
--accent: hsl(40 9% 93.5%);
|
|
40
|
+
--accent-foreground: hsl(60 1% 17%);
|
|
46
41
|
/* Destructive — Claude danger-100 hsl(0 61% 52%). Slightly deeper for a solid
|
|
47
42
|
* white-on-fill AA pass. */
|
|
48
|
-
--destructive: hsl(
|
|
49
|
-
--destructive-foreground: hsl(0 0%
|
|
50
|
-
--border: hsl(
|
|
43
|
+
--destructive: hsl(5 62% 48%);
|
|
44
|
+
--destructive-foreground: hsl(0 0% 100%);
|
|
45
|
+
--border: hsl(40 6% 90%);
|
|
51
46
|
/* Input surface — faint warm fill so fields are visible on white cards. */
|
|
52
|
-
--input: hsl(
|
|
47
|
+
--input: hsl(40 6% 90%);
|
|
53
48
|
/* Divider — hairline between rows; slightly darker than --border so it reads
|
|
54
49
|
* on white cards. */
|
|
55
|
-
--divider: hsl(
|
|
50
|
+
--divider: hsl(40 6% 92%);
|
|
56
51
|
/* Overlay — modal scrim / backdrop behind dialogs, drawers, sheets. A scrim
|
|
57
52
|
* is black in both themes; the token owns the opacity. */
|
|
58
|
-
--overlay: hsl(0 0% 0% / 0.
|
|
53
|
+
--overlay: hsl(0 0% 0% / 0.4);
|
|
59
54
|
/* Focus ring — blue (system-accent feel), independent of the cyan brand. */
|
|
60
|
-
--ring: hsl(
|
|
55
|
+
--ring: hsl(209 74% 52%);
|
|
61
56
|
--shadow-card: 0 1px 3px 0 rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.04);
|
|
62
57
|
|
|
63
58
|
/* Code surface — used by markdown code fences, terminal blocks,
|
|
@@ -70,23 +65,23 @@
|
|
|
70
65
|
* for body legibility. Border keeps the panel anchored.
|
|
71
66
|
* Inline code shares the same surface for chip-on-chip cohesion.
|
|
72
67
|
*/
|
|
73
|
-
--code: hsl(
|
|
74
|
-
--code-foreground: hsl(
|
|
75
|
-
--code-border: hsl(
|
|
76
|
-
--code-inline: hsl(
|
|
77
|
-
--code-inline-foreground: hsl(
|
|
68
|
+
--code: hsl(30 14% 97%);
|
|
69
|
+
--code-foreground: hsl(60 1% 17%);
|
|
70
|
+
--code-border: hsl(40 6% 90%);
|
|
71
|
+
--code-inline: hsl(40 9% 93.5%);
|
|
72
|
+
--code-inline-foreground: hsl(5 62% 42%);
|
|
78
73
|
/* Sidebar Light Theme — warm off-white, same family as the page (Claude keeps
|
|
79
74
|
* rail and page in one tone). */
|
|
80
|
-
--sidebar-background: hsl(
|
|
81
|
-
--sidebar-foreground: hsl(
|
|
82
|
-
--sidebar-primary: hsl(
|
|
75
|
+
--sidebar-background: hsl(30 14% 97%);
|
|
76
|
+
--sidebar-foreground: hsl(60 1% 17%);
|
|
77
|
+
--sidebar-primary: hsl(209 74% 47%);
|
|
83
78
|
--sidebar-primary-foreground: hsl(0 0% 100%);
|
|
84
79
|
/* 90%, a clear (≥4L) step below the 96% rail — hover stays visible. See the
|
|
85
80
|
2026-08-03 note: the dark pair steps ~12 points, so light must step too. */
|
|
86
|
-
--sidebar-accent: hsl(
|
|
87
|
-
--sidebar-accent-foreground: hsl(
|
|
88
|
-
--sidebar-border: hsl(
|
|
89
|
-
--sidebar-ring: hsl(
|
|
81
|
+
--sidebar-accent: hsl(40 7% 92%);
|
|
82
|
+
--sidebar-accent-foreground: hsl(60 1% 17%);
|
|
83
|
+
--sidebar-border: hsl(40 6% 90%);
|
|
84
|
+
--sidebar-ring: hsl(209 74% 52%);
|
|
90
85
|
/* ── Status surfaces ─────────────────────────────────────────────
|
|
91
86
|
* Each status has 4 tokens: icon/accent color, page background,
|
|
92
87
|
* readable foreground text, and border ring.
|
|
@@ -100,23 +95,23 @@
|
|
|
100
95
|
* ─────────────────────────────────────────────────────────────── */
|
|
101
96
|
|
|
102
97
|
/* Warning — amber */
|
|
103
|
-
--warning: hsl(
|
|
104
|
-
--warning-background: hsl(
|
|
105
|
-
--warning-foreground: hsl(
|
|
106
|
-
--warning-border: hsl(
|
|
98
|
+
--warning: hsl(39 60% 51%);
|
|
99
|
+
--warning-background: hsl(52 70% 96%);
|
|
100
|
+
--warning-foreground: hsl(45 53% 26%);
|
|
101
|
+
--warning-border: hsl(46 66% 82%);
|
|
107
102
|
/* Success — green */
|
|
108
|
-
--success: hsl(
|
|
109
|
-
--success-background: hsl(
|
|
110
|
-
--success-foreground: hsl(
|
|
111
|
-
--success-border: hsl(
|
|
103
|
+
--success: hsl(147 30% 47%);
|
|
104
|
+
--success-background: hsl(147 20% 96%);
|
|
105
|
+
--success-foreground: hsl(147 33% 25%);
|
|
106
|
+
--success-border: hsl(147 22% 87%);
|
|
112
107
|
/* Destructive surface (separate from the filled button token) */
|
|
113
|
-
--destructive-background: hsl(0
|
|
114
|
-
--destructive-border: hsl(
|
|
108
|
+
--destructive-background: hsl(0 55% 97%);
|
|
109
|
+
--destructive-border: hsl(5 70% 92%);
|
|
115
110
|
/* Info — blue-cyan (brand-adjacent) */
|
|
116
|
-
--info: hsl(
|
|
117
|
-
--info-background: hsl(
|
|
118
|
-
--info-foreground: hsl(
|
|
119
|
-
--info-border: hsl(
|
|
111
|
+
--info: hsl(209 74% 52%);
|
|
112
|
+
--info-background: hsl(209 80% 97%);
|
|
113
|
+
--info-foreground: hsl(209 50% 29%);
|
|
114
|
+
--info-border: hsl(209 75% 91%);
|
|
120
115
|
|
|
121
116
|
/* On-fill text/icon — readable color sitting ON the solid `*` fill
|
|
122
117
|
* (badges, unread pills, filled chips), distinct from the `*-foreground`
|
|
@@ -129,16 +124,16 @@
|
|
|
129
124
|
--on-success: hsl(0 0% 9%);
|
|
130
125
|
--on-warning: hsl(0 0% 9%);
|
|
131
126
|
--on-info: hsl(0 0% 9%);
|
|
132
|
-
--on-destructive: hsl(0 0%
|
|
127
|
+
--on-destructive: hsl(0 0% 100%);
|
|
133
128
|
/* Surface gradient */
|
|
134
129
|
--surface-gradient: linear-gradient(to bottom, var(--background), color-mix(in oklab, var(--background) 80%, transparent));
|
|
135
130
|
|
|
136
131
|
/* Chart colors — chart-1 aligned with brand hue.
|
|
137
132
|
* Fully-wrapped hsl() like every other token (consume via `bg-chart-N` or
|
|
138
133
|
* `var(--chart-N)`, never `hsl(var(--chart-N))`). */
|
|
139
|
-
--chart-1: hsl(
|
|
140
|
-
--chart-2: hsl(
|
|
141
|
-
--chart-3: hsl(
|
|
142
|
-
--chart-4: hsl(
|
|
143
|
-
--chart-5: hsl(
|
|
134
|
+
--chart-1: hsl(209 74% 52%);
|
|
135
|
+
--chart-2: hsl(147 30% 47%);
|
|
136
|
+
--chart-3: hsl(277 30% 55%);
|
|
137
|
+
--chart-4: hsl(31 65% 51%);
|
|
138
|
+
--chart-5: hsl(5 62% 59%);
|
|
144
139
|
}
|