@lifanh/quiet-paper 0.1.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 +86 -0
- package/dist/index.d.ts +94 -0
- package/dist/quiet-paper.js +226 -0
- package/dist/quiet-paper.js.map +1 -0
- package/package.json +50 -0
- package/styles/tailwind-sources.css +8 -0
- package/styles/tailwind-theme.css +48 -0
- package/styles/tokens.css +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @lifanh/quiet-paper
|
|
2
|
+
|
|
3
|
+
**Quiet paper** design system — React primitives and Tailwind v4 tokens for personal sites and app demos.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- React 18 or 19
|
|
8
|
+
- Tailwind CSS v4 with utilities used by components (`bg-accent`, `font-ui`, `text-muted`, etc.)
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @lifanh/quiet-paper
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Local development** (link while editing the package):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install /path/to/quiet-paper
|
|
20
|
+
# or: npm link @lifanh/quiet-paper
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Styles (once per app)
|
|
24
|
+
|
|
25
|
+
In `global.css`:
|
|
26
|
+
|
|
27
|
+
```css
|
|
28
|
+
@import "tailwindcss";
|
|
29
|
+
@import "@lifanh/quiet-paper/styles/tailwind-sources.css";
|
|
30
|
+
@import "@lifanh/quiet-paper/styles/tokens.css" layer(theme);
|
|
31
|
+
@import "@lifanh/quiet-paper/styles/tailwind-theme.css";
|
|
32
|
+
|
|
33
|
+
@layer base {
|
|
34
|
+
/* document defaults — see lifan-astro-minimal-theme/src/styles/global.css */
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Use
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import {
|
|
42
|
+
Button,
|
|
43
|
+
Field,
|
|
44
|
+
Panel,
|
|
45
|
+
EmptyState,
|
|
46
|
+
ErrorState,
|
|
47
|
+
HairlineTable,
|
|
48
|
+
} from "@lifanh/quiet-paper";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## What stays in the app
|
|
52
|
+
|
|
53
|
+
- Astro layouts, marketing pages, demo-specific composites (`FetchDemo`, `RunsTable`, …)
|
|
54
|
+
- This package is **primitives only** — no business logic
|
|
55
|
+
|
|
56
|
+
## Canonical tokens
|
|
57
|
+
|
|
58
|
+
Edit `styles/tokens.css` here first; apps should not fork hex values in components.
|
|
59
|
+
|
|
60
|
+
## Why `tailwind-sources.css`?
|
|
61
|
+
|
|
62
|
+
Tailwind v4 does not scan `node_modules`. The package ships a one-line `@source "../src"` file so consumers never hard-code `../../node_modules/...` (breaks when `global.css` moves or you use pnpm).
|
|
63
|
+
|
|
64
|
+
## Consumer contract (Pattern A only)
|
|
65
|
+
|
|
66
|
+
All consumers are expected to use **Tailwind CSS v4**. Setup:
|
|
67
|
+
|
|
68
|
+
```css
|
|
69
|
+
@import "tailwindcss";
|
|
70
|
+
@import "@lifanh/quiet-paper/styles/tailwind-sources.css";
|
|
71
|
+
@import "@lifanh/quiet-paper/styles/tokens.css" layer(theme);
|
|
72
|
+
@import "@lifanh/quiet-paper/styles/tailwind-theme.css";
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
No pre-built utility CSS in this package; the app’s Tailwind build generates utilities after scanning package sources via `tailwind-sources.css`.
|
|
76
|
+
|
|
77
|
+
**Do not** add `@source` paths into `node_modules` from the app — the package owns that via `tailwind-sources.css`.
|
|
78
|
+
|
|
79
|
+
## Contrast
|
|
80
|
+
|
|
81
|
+
After editing `styles/tokens.css`, re-check foreground/background pairs (WCAG AA 4.5:1 for normal text).
|
|
82
|
+
|
|
83
|
+
## Docs
|
|
84
|
+
|
|
85
|
+
- [Architecture](./docs/ARCHITECTURE.md)
|
|
86
|
+
- [Publish](./docs/PUBLISH.md)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ButtonHTMLAttributes } from 'react';
|
|
2
|
+
import { InputHTMLAttributes } from 'react';
|
|
3
|
+
import { JSX } from 'react';
|
|
4
|
+
import { ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
export declare function Button({ children, variant, className, type, ...props }: ButtonProps): JSX.Element;
|
|
7
|
+
|
|
8
|
+
export declare type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
variant?: Variant;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export declare function EmptyState({ title, description, primaryAction, secondaryAction, children, }: EmptyStateProps): JSX.Element;
|
|
14
|
+
|
|
15
|
+
export declare type EmptyStateAction = {
|
|
16
|
+
label: string;
|
|
17
|
+
onClick: () => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export declare type EmptyStateProps = {
|
|
21
|
+
title: string;
|
|
22
|
+
description: string;
|
|
23
|
+
primaryAction?: EmptyStateAction;
|
|
24
|
+
secondaryAction?: EmptyStateAction;
|
|
25
|
+
children?: ReactNode;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export declare function ErrorState({ kicker, title, description, detail, primaryAction, secondaryAction, children, }: ErrorStateProps): JSX.Element;
|
|
29
|
+
|
|
30
|
+
export declare type ErrorStateAction = {
|
|
31
|
+
label: string;
|
|
32
|
+
onClick: () => void;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export declare type ErrorStateProps = {
|
|
36
|
+
/** Short status label above the title (i18n). Omit with `kicker=""` to hide. */
|
|
37
|
+
kicker?: string;
|
|
38
|
+
title: string;
|
|
39
|
+
description: string;
|
|
40
|
+
detail?: string;
|
|
41
|
+
primaryAction?: ErrorStateAction;
|
|
42
|
+
secondaryAction?: ErrorStateAction;
|
|
43
|
+
children?: ReactNode;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export declare function Field({ id, label, hint, error, className, "aria-describedby": ariaDescribedBy, ...inputProps }: FieldProps): JSX.Element;
|
|
47
|
+
|
|
48
|
+
export declare type FieldProps = {
|
|
49
|
+
id: string;
|
|
50
|
+
label: string;
|
|
51
|
+
hint?: string;
|
|
52
|
+
/** Inline validation — one line under the input, no box */
|
|
53
|
+
error?: string;
|
|
54
|
+
} & InputHTMLAttributes<HTMLInputElement>;
|
|
55
|
+
|
|
56
|
+
export declare type HairlineColumn<T> = {
|
|
57
|
+
id: string;
|
|
58
|
+
header: string;
|
|
59
|
+
align?: "left" | "right";
|
|
60
|
+
cell: (row: T) => ReactNode;
|
|
61
|
+
headerClassName?: string;
|
|
62
|
+
cellClassName?: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export declare function HairlineTable<T>({ caption, columns, rows, getRowKey, empty, }: HairlineTableProps<T>): JSX.Element;
|
|
66
|
+
|
|
67
|
+
export declare type HairlineTableProps<T> = {
|
|
68
|
+
caption?: string;
|
|
69
|
+
columns: HairlineColumn<T>[];
|
|
70
|
+
rows: T[];
|
|
71
|
+
getRowKey: (row: T) => string;
|
|
72
|
+
empty?: ReactNode;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export declare function LoadingBlock({ label, lines, className, }: LoadingBlockProps): JSX.Element;
|
|
76
|
+
|
|
77
|
+
export declare type LoadingBlockProps = {
|
|
78
|
+
/** Accessible label for screen readers */
|
|
79
|
+
label?: string;
|
|
80
|
+
/** Number of placeholder lines */
|
|
81
|
+
lines?: number;
|
|
82
|
+
className?: string;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export declare function Panel({ children, className }: PanelProps): JSX.Element;
|
|
86
|
+
|
|
87
|
+
export declare type PanelProps = {
|
|
88
|
+
children: ReactNode;
|
|
89
|
+
className?: string;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
declare type Variant = "primary" | "secondary";
|
|
93
|
+
|
|
94
|
+
export { }
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { jsx as e, jsxs as d, Fragment as b } from "react/jsx-runtime";
|
|
2
|
+
const x = {
|
|
3
|
+
primary: "bg-accent text-surface hover:bg-accent-hover border border-transparent",
|
|
4
|
+
secondary: "bg-transparent text-text border-border hover:border-text"
|
|
5
|
+
};
|
|
6
|
+
function o({
|
|
7
|
+
children: t,
|
|
8
|
+
variant: a = "primary",
|
|
9
|
+
className: l = "",
|
|
10
|
+
type: r = "button",
|
|
11
|
+
...n
|
|
12
|
+
}) {
|
|
13
|
+
return /* @__PURE__ */ e(
|
|
14
|
+
"button",
|
|
15
|
+
{
|
|
16
|
+
type: r,
|
|
17
|
+
className: `font-ui inline-flex items-center justify-center rounded-sm border px-4 py-2 text-sm leading-none transition-colors ${x[a]} ${l}`.trim(),
|
|
18
|
+
...n,
|
|
19
|
+
children: t
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
function h({
|
|
24
|
+
id: t,
|
|
25
|
+
label: a,
|
|
26
|
+
hint: l,
|
|
27
|
+
error: r,
|
|
28
|
+
className: n = "",
|
|
29
|
+
"aria-describedby": i,
|
|
30
|
+
...s
|
|
31
|
+
}) {
|
|
32
|
+
const c = r ? `${t}-error` : void 0, m = l && !r ? `${t}-hint` : void 0, u = [c, m, i].filter(Boolean).join(" ") || void 0;
|
|
33
|
+
return /* @__PURE__ */ d("div", { className: "space-y-2", children: [
|
|
34
|
+
/* @__PURE__ */ e("label", { htmlFor: t, className: "font-ui block text-sm text-muted", children: a }),
|
|
35
|
+
/* @__PURE__ */ e(
|
|
36
|
+
"input",
|
|
37
|
+
{
|
|
38
|
+
id: t,
|
|
39
|
+
"aria-invalid": r ? !0 : void 0,
|
|
40
|
+
"aria-describedby": u,
|
|
41
|
+
className: `font-ui w-full rounded-sm border bg-bg px-3 py-2 text-sm text-text placeholder:text-muted/70 focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-2 ${r ? "border-2 border-error-border focus-visible:outline-error" : "border border-border focus-visible:outline-accent"} ${n}`.trim(),
|
|
42
|
+
...s
|
|
43
|
+
}
|
|
44
|
+
),
|
|
45
|
+
r ? /* @__PURE__ */ d(
|
|
46
|
+
"p",
|
|
47
|
+
{
|
|
48
|
+
id: c,
|
|
49
|
+
className: "font-ui m-0 text-xs font-medium leading-normal text-error",
|
|
50
|
+
role: "alert",
|
|
51
|
+
children: [
|
|
52
|
+
/* @__PURE__ */ d("span", { className: "font-mono font-bold", "aria-hidden": !0, children: [
|
|
53
|
+
"×",
|
|
54
|
+
" "
|
|
55
|
+
] }),
|
|
56
|
+
r
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
) : l ? /* @__PURE__ */ e("p", { id: m, className: "font-ui m-0 text-xs leading-normal text-muted", children: l }) : null
|
|
60
|
+
] });
|
|
61
|
+
}
|
|
62
|
+
function p({ children: t, className: a = "" }) {
|
|
63
|
+
return /* @__PURE__ */ e(
|
|
64
|
+
"section",
|
|
65
|
+
{
|
|
66
|
+
className: `rounded-md border border-border-subtle bg-surface p-5 ${a}`.trim(),
|
|
67
|
+
children: t
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
function g({
|
|
72
|
+
title: t,
|
|
73
|
+
description: a,
|
|
74
|
+
primaryAction: l,
|
|
75
|
+
secondaryAction: r,
|
|
76
|
+
children: n
|
|
77
|
+
}) {
|
|
78
|
+
return /* @__PURE__ */ d(
|
|
79
|
+
"div",
|
|
80
|
+
{
|
|
81
|
+
className: "rounded-md border border-dashed border-border py-10 px-5 text-center",
|
|
82
|
+
role: "status",
|
|
83
|
+
children: [
|
|
84
|
+
/* @__PURE__ */ e("h3", { className: "font-ui m-0 text-sm font-semibold text-text", children: t }),
|
|
85
|
+
/* @__PURE__ */ e("p", { className: "font-ui m-0 mt-2 text-sm leading-relaxed text-muted", children: a }),
|
|
86
|
+
n,
|
|
87
|
+
(l || r) && /* @__PURE__ */ d("div", { className: "mt-5 flex flex-wrap items-center justify-center gap-3", children: [
|
|
88
|
+
l ? /* @__PURE__ */ e(o, { type: "button", onClick: l.onClick, children: l.label }) : null,
|
|
89
|
+
r ? /* @__PURE__ */ e(
|
|
90
|
+
o,
|
|
91
|
+
{
|
|
92
|
+
type: "button",
|
|
93
|
+
variant: "secondary",
|
|
94
|
+
onClick: r.onClick,
|
|
95
|
+
children: r.label
|
|
96
|
+
}
|
|
97
|
+
) : null
|
|
98
|
+
] })
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
function N({
|
|
104
|
+
kicker: t = "Failed",
|
|
105
|
+
title: a,
|
|
106
|
+
description: l,
|
|
107
|
+
detail: r,
|
|
108
|
+
primaryAction: n,
|
|
109
|
+
secondaryAction: i,
|
|
110
|
+
children: s
|
|
111
|
+
}) {
|
|
112
|
+
return /* @__PURE__ */ d(
|
|
113
|
+
"div",
|
|
114
|
+
{
|
|
115
|
+
className: "rounded-md border border-error-border bg-error-surface px-5 py-6",
|
|
116
|
+
role: "alert",
|
|
117
|
+
children: [
|
|
118
|
+
t ? /* @__PURE__ */ d("p", { className: "font-ui m-0 flex items-center gap-2 text-xs font-semibold tracking-[0.14em] text-error uppercase", children: [
|
|
119
|
+
/* @__PURE__ */ e(
|
|
120
|
+
"span",
|
|
121
|
+
{
|
|
122
|
+
className: "inline-flex size-4 shrink-0 items-center justify-center rounded-sm border border-error-border bg-bg font-mono text-[0.65rem] font-bold leading-none text-error",
|
|
123
|
+
"aria-hidden": !0,
|
|
124
|
+
children: "!"
|
|
125
|
+
}
|
|
126
|
+
),
|
|
127
|
+
/* @__PURE__ */ e("span", { children: t })
|
|
128
|
+
] }) : null,
|
|
129
|
+
/* @__PURE__ */ e(
|
|
130
|
+
"h3",
|
|
131
|
+
{
|
|
132
|
+
className: `font-ui m-0 text-sm font-semibold text-error ${t ? "mt-2" : ""}`.trim(),
|
|
133
|
+
children: a
|
|
134
|
+
}
|
|
135
|
+
),
|
|
136
|
+
/* @__PURE__ */ e("p", { className: "font-ui m-0 mt-2 text-sm leading-relaxed text-text", children: l }),
|
|
137
|
+
r ? /* @__PURE__ */ e("p", { className: "font-ui m-0 mt-2 font-mono text-xs leading-normal text-muted", children: r }) : null,
|
|
138
|
+
s,
|
|
139
|
+
(n || i) && /* @__PURE__ */ d("div", { className: "mt-5 flex flex-wrap items-center gap-3", children: [
|
|
140
|
+
n ? /* @__PURE__ */ e(o, { type: "button", onClick: n.onClick, children: n.label }) : null,
|
|
141
|
+
i ? /* @__PURE__ */ e(
|
|
142
|
+
o,
|
|
143
|
+
{
|
|
144
|
+
type: "button",
|
|
145
|
+
variant: "secondary",
|
|
146
|
+
onClick: i.onClick,
|
|
147
|
+
children: i.label
|
|
148
|
+
}
|
|
149
|
+
) : null
|
|
150
|
+
] })
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
function v({
|
|
156
|
+
caption: t,
|
|
157
|
+
columns: a,
|
|
158
|
+
rows: l,
|
|
159
|
+
getRowKey: r,
|
|
160
|
+
empty: n
|
|
161
|
+
}) {
|
|
162
|
+
return l.length === 0 && n ? /* @__PURE__ */ e(b, { children: n }) : /* @__PURE__ */ e("div", { className: "overflow-x-auto", children: /* @__PURE__ */ d("table", { className: "w-full min-w-[20rem] border-collapse text-left", children: [
|
|
163
|
+
t ? /* @__PURE__ */ e("caption", { className: "sr-only", children: t }) : null,
|
|
164
|
+
/* @__PURE__ */ e("thead", { children: /* @__PURE__ */ e("tr", { className: "border-b border-border", children: a.map((i) => /* @__PURE__ */ e(
|
|
165
|
+
"th",
|
|
166
|
+
{
|
|
167
|
+
scope: "col",
|
|
168
|
+
className: `font-ui pb-2 text-xs font-semibold tracking-wide text-muted uppercase ${i.align === "right" ? "text-right" : "text-left"} ${i.headerClassName ?? ""}`.trim(),
|
|
169
|
+
children: i.header
|
|
170
|
+
},
|
|
171
|
+
i.id
|
|
172
|
+
)) }) }),
|
|
173
|
+
/* @__PURE__ */ e("tbody", { children: l.map((i) => /* @__PURE__ */ e(
|
|
174
|
+
"tr",
|
|
175
|
+
{
|
|
176
|
+
className: "border-b border-border-subtle last:border-b-0",
|
|
177
|
+
children: a.map((s) => /* @__PURE__ */ e(
|
|
178
|
+
"td",
|
|
179
|
+
{
|
|
180
|
+
className: `font-ui py-3 text-sm text-text ${s.align === "right" ? "text-right tabular-nums" : "text-left"} ${s.cellClassName ?? ""}`.trim(),
|
|
181
|
+
children: s.cell(i)
|
|
182
|
+
},
|
|
183
|
+
s.id
|
|
184
|
+
))
|
|
185
|
+
},
|
|
186
|
+
r(i)
|
|
187
|
+
)) })
|
|
188
|
+
] }) });
|
|
189
|
+
}
|
|
190
|
+
function y({
|
|
191
|
+
label: t = "Loading",
|
|
192
|
+
lines: a = 3,
|
|
193
|
+
className: l = ""
|
|
194
|
+
}) {
|
|
195
|
+
return /* @__PURE__ */ d(
|
|
196
|
+
"div",
|
|
197
|
+
{
|
|
198
|
+
className: `space-y-3 ${l}`.trim(),
|
|
199
|
+
role: "status",
|
|
200
|
+
"aria-live": "polite",
|
|
201
|
+
"aria-busy": "true",
|
|
202
|
+
children: [
|
|
203
|
+
/* @__PURE__ */ e("span", { className: "sr-only", children: t }),
|
|
204
|
+
Array.from({ length: a }, (r, n) => /* @__PURE__ */ e(
|
|
205
|
+
"div",
|
|
206
|
+
{
|
|
207
|
+
className: "h-3 animate-pulse rounded-sm bg-border-subtle",
|
|
208
|
+
style: { width: n === a - 1 ? "72%" : "100%" },
|
|
209
|
+
"aria-hidden": "true"
|
|
210
|
+
},
|
|
211
|
+
n
|
|
212
|
+
))
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
export {
|
|
218
|
+
o as Button,
|
|
219
|
+
g as EmptyState,
|
|
220
|
+
N as ErrorState,
|
|
221
|
+
h as Field,
|
|
222
|
+
v as HairlineTable,
|
|
223
|
+
y as LoadingBlock,
|
|
224
|
+
p as Panel
|
|
225
|
+
};
|
|
226
|
+
//# sourceMappingURL=quiet-paper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quiet-paper.js","sources":["../src/Button.tsx","../src/Field.tsx","../src/Panel.tsx","../src/EmptyState.tsx","../src/ErrorState.tsx","../src/HairlineTable.tsx","../src/LoadingBlock.tsx"],"sourcesContent":["import type { ButtonHTMLAttributes, ReactNode } from \"react\";\n\ntype Variant = \"primary\" | \"secondary\";\n\nexport type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {\n\tchildren: ReactNode;\n\tvariant?: Variant;\n};\n\nconst variants: Record<Variant, string> = {\n\tprimary:\n\t\t\"bg-accent text-surface hover:bg-accent-hover border border-transparent\",\n\tsecondary: \"bg-transparent text-text border-border hover:border-text\",\n};\n\nexport function Button({\n\tchildren,\n\tvariant = \"primary\",\n\tclassName = \"\",\n\ttype = \"button\",\n\t...props\n}: ButtonProps) {\n\treturn (\n\t\t<button\n\t\t\ttype={type}\n\t\t\tclassName={`font-ui inline-flex items-center justify-center rounded-sm border px-4 py-2 text-sm leading-none transition-colors ${variants[variant]} ${className}`.trim()}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</button>\n\t);\n}","import type { InputHTMLAttributes } from \"react\";\n\nexport type FieldProps = {\n\tid: string;\n\tlabel: string;\n\thint?: string;\n\t/** Inline validation — one line under the input, no box */\n\terror?: string;\n} & InputHTMLAttributes<HTMLInputElement>;\n\nexport function Field({\n\tid,\n\tlabel,\n\thint,\n\terror,\n\tclassName = \"\",\n\t\"aria-describedby\": ariaDescribedBy,\n\t...inputProps\n}: FieldProps) {\n\tconst errorId = error ? `${id}-error` : undefined;\n\tconst hintId = hint && !error ? `${id}-hint` : undefined;\n\tconst describedBy =\n\t\t[errorId, hintId, ariaDescribedBy].filter(Boolean).join(\" \") || undefined;\n\n\treturn (\n\t\t<div className=\"space-y-2\">\n\t\t\t<label htmlFor={id} className=\"font-ui block text-sm text-muted\">\n\t\t\t\t{label}\n\t\t\t</label>\n\t\t\t<input\n\t\t\t\tid={id}\n\t\t\t\taria-invalid={error ? true : undefined}\n\t\t\t\taria-describedby={describedBy}\n\t\t\t\tclassName={`font-ui w-full rounded-sm border bg-bg px-3 py-2 text-sm text-text placeholder:text-muted/70 focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-2 ${error ? \"border-2 border-error-border focus-visible:outline-error\" : \"border border-border focus-visible:outline-accent\"} ${className}`.trim()}\n\t\t\t\t{...inputProps}\n\t\t\t/>\n\t\t\t{error ? (\n\t\t\t\t<p\n\t\t\t\t\tid={errorId}\n\t\t\t\t\tclassName=\"font-ui m-0 text-xs font-medium leading-normal text-error\"\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t>\n\t\t\t\t\t<span className=\"font-mono font-bold\" aria-hidden>\n\t\t\t\t\t\t×{\" \"}\n\t\t\t\t\t</span>\n\t\t\t\t\t{error}\n\t\t\t\t</p>\n\t\t\t) : hint ? (\n\t\t\t\t<p id={hintId} className=\"font-ui m-0 text-xs leading-normal text-muted\">\n\t\t\t\t\t{hint}\n\t\t\t\t</p>\n\t\t\t) : null}\n\t\t</div>\n\t);\n}","import type { ReactNode } from \"react\";\n\nexport type PanelProps = {\n\tchildren: ReactNode;\n\tclassName?: string;\n};\n\nexport function Panel({ children, className = \"\" }: PanelProps) {\n\treturn (\n\t\t<section\n\t\t\tclassName={`rounded-md border border-border-subtle bg-surface p-5 ${className}`.trim()}\n\t\t>\n\t\t\t{children}\n\t\t</section>\n\t);\n}","import type { ReactNode } from \"react\";\nimport { Button } from \"./Button\";\n\nexport type EmptyStateAction = {\n\tlabel: string;\n\tonClick: () => void;\n};\n\nexport type EmptyStateProps = {\n\ttitle: string;\n\tdescription: string;\n\tprimaryAction?: EmptyStateAction;\n\tsecondaryAction?: EmptyStateAction;\n\tchildren?: ReactNode;\n};\n\nexport function EmptyState({\n\ttitle,\n\tdescription,\n\tprimaryAction,\n\tsecondaryAction,\n\tchildren,\n}: EmptyStateProps) {\n\treturn (\n\t\t<div\n\t\t\tclassName=\"rounded-md border border-dashed border-border py-10 px-5 text-center\"\n\t\t\trole=\"status\"\n\t\t>\n\t\t\t<h3 className=\"font-ui m-0 text-sm font-semibold text-text\">{title}</h3>\n\t\t\t<p className=\"font-ui m-0 mt-2 text-sm leading-relaxed text-muted\">\n\t\t\t\t{description}\n\t\t\t</p>\n\t\t\t{children}\n\t\t\t{(primaryAction || secondaryAction) && (\n\t\t\t\t<div className=\"mt-5 flex flex-wrap items-center justify-center gap-3\">\n\t\t\t\t\t{primaryAction ? (\n\t\t\t\t\t\t<Button type=\"button\" onClick={primaryAction.onClick}>\n\t\t\t\t\t\t\t{primaryAction.label}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{secondaryAction ? (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\tonClick={secondaryAction.onClick}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{secondaryAction.label}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}","import type { ReactNode } from \"react\";\nimport { Button } from \"./Button\";\n\nexport type ErrorStateAction = {\n\tlabel: string;\n\tonClick: () => void;\n};\n\nexport type ErrorStateProps = {\n\t/** Short status label above the title (i18n). Omit with `kicker=\"\"` to hide. */\n\tkicker?: string;\n\ttitle: string;\n\tdescription: string;\n\tdetail?: string;\n\tprimaryAction?: ErrorStateAction;\n\tsecondaryAction?: ErrorStateAction;\n\tchildren?: ReactNode;\n};\n\nexport function ErrorState({\n\tkicker = \"Failed\",\n\ttitle,\n\tdescription,\n\tdetail,\n\tprimaryAction,\n\tsecondaryAction,\n\tchildren,\n}: ErrorStateProps) {\n\treturn (\n\t\t<div\n\t\t\tclassName=\"rounded-md border border-error-border bg-error-surface px-5 py-6\"\n\t\t\trole=\"alert\"\n\t\t>\n\t\t\t{kicker ? (\n\t\t\t\t<p className=\"font-ui m-0 flex items-center gap-2 text-xs font-semibold tracking-[0.14em] text-error uppercase\">\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName=\"inline-flex size-4 shrink-0 items-center justify-center rounded-sm border border-error-border bg-bg font-mono text-[0.65rem] font-bold leading-none text-error\"\n\t\t\t\t\t\taria-hidden\n\t\t\t\t\t>\n\t\t\t\t\t\t!\n\t\t\t\t\t</span>\n\t\t\t\t\t<span>{kicker}</span>\n\t\t\t\t</p>\n\t\t\t) : null}\n\t\t\t<h3\n\t\t\t\tclassName={`font-ui m-0 text-sm font-semibold text-error ${kicker ? \"mt-2\" : \"\"}`.trim()}\n\t\t\t>\n\t\t\t\t{title}\n\t\t\t</h3>\n\t\t\t<p className=\"font-ui m-0 mt-2 text-sm leading-relaxed text-text\">\n\t\t\t\t{description}\n\t\t\t</p>\n\t\t\t{detail ? (\n\t\t\t\t<p className=\"font-ui m-0 mt-2 font-mono text-xs leading-normal text-muted\">\n\t\t\t\t\t{detail}\n\t\t\t\t</p>\n\t\t\t) : null}\n\t\t\t{children}\n\t\t\t{(primaryAction || secondaryAction) && (\n\t\t\t\t<div className=\"mt-5 flex flex-wrap items-center gap-3\">\n\t\t\t\t\t{primaryAction ? (\n\t\t\t\t\t\t<Button type=\"button\" onClick={primaryAction.onClick}>\n\t\t\t\t\t\t\t{primaryAction.label}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{secondaryAction ? (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\tonClick={secondaryAction.onClick}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{secondaryAction.label}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}","import type { ReactNode } from \"react\";\n\nexport type HairlineColumn<T> = {\n\tid: string;\n\theader: string;\n\talign?: \"left\" | \"right\";\n\tcell: (row: T) => ReactNode;\n\theaderClassName?: string;\n\tcellClassName?: string;\n};\n\nexport type HairlineTableProps<T> = {\n\tcaption?: string;\n\tcolumns: HairlineColumn<T>[];\n\trows: T[];\n\tgetRowKey: (row: T) => string;\n\tempty?: ReactNode;\n};\n\nexport function HairlineTable<T>({\n\tcaption,\n\tcolumns,\n\trows,\n\tgetRowKey,\n\tempty,\n}: HairlineTableProps<T>) {\n\tif (rows.length === 0 && empty) {\n\t\treturn <>{empty}</>;\n\t}\n\n\treturn (\n\t\t<div className=\"overflow-x-auto\">\n\t\t\t<table className=\"w-full min-w-[20rem] border-collapse text-left\">\n\t\t\t\t{caption ? (\n\t\t\t\t\t<caption className=\"sr-only\">{caption}</caption>\n\t\t\t\t) : null}\n\t\t\t\t<thead>\n\t\t\t\t\t<tr className=\"border-b border-border\">\n\t\t\t\t\t\t{columns.map((col) => (\n\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\tkey={col.id}\n\t\t\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\t\t\tclassName={`font-ui pb-2 text-xs font-semibold tracking-wide text-muted uppercase ${col.align === \"right\" ? \"text-right\" : \"text-left\"} ${col.headerClassName ?? \"\"}`.trim()}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{col.header}\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{rows.map((row) => (\n\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\tkey={getRowKey(row)}\n\t\t\t\t\t\t\tclassName=\"border-b border-border-subtle last:border-b-0\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{columns.map((col) => (\n\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\tkey={col.id}\n\t\t\t\t\t\t\t\t\tclassName={`font-ui py-3 text-sm text-text ${col.align === \"right\" ? \"text-right tabular-nums\" : \"text-left\"} ${col.cellClassName ?? \"\"}`.trim()}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{col.cell(row)}\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}","export type LoadingBlockProps = {\n\t/** Accessible label for screen readers */\n\tlabel?: string;\n\t/** Number of placeholder lines */\n\tlines?: number;\n\tclassName?: string;\n};\n\nexport function LoadingBlock({\n\tlabel = \"Loading\",\n\tlines = 3,\n\tclassName = \"\",\n}: LoadingBlockProps) {\n\treturn (\n\t\t<div\n\t\t\tclassName={`space-y-3 ${className}`.trim()}\n\t\t\trole=\"status\"\n\t\t\taria-live=\"polite\"\n\t\t\taria-busy=\"true\"\n\t\t>\n\t\t\t<span className=\"sr-only\">{label}</span>\n\t\t\t{Array.from({ length: lines }, (_, i) => (\n\t\t\t\t<div\n\t\t\t\t\tkey={i}\n\t\t\t\t\tclassName=\"h-3 animate-pulse rounded-sm bg-border-subtle\"\n\t\t\t\t\tstyle={{ width: i === lines - 1 ? \"72%\" : \"100%\" }}\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t/>\n\t\t\t))}\n\t\t</div>\n\t);\n}"],"names":["variants","Button","children","variant","className","type","props","jsx","Field","id","label","hint","error","ariaDescribedBy","inputProps","errorId","hintId","describedBy","jsxs","Panel","EmptyState","title","description","primaryAction","secondaryAction","ErrorState","kicker","detail","HairlineTable","caption","columns","rows","getRowKey","empty","col","row","LoadingBlock","lines","_","i"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lifanh/quiet-paper",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Quiet paper design system — Tailwind v4 tokens + React primitives (Panel, Field, Button, states, HairlineTable)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/lifanh/quiet-paper.git"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public",
|
|
13
|
+
"registry": "https://registry.npmjs.org/"
|
|
14
|
+
},
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"**/*.css"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "vite build",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/quiet-paper.js",
|
|
27
|
+
"default": "./dist/quiet-paper.js"
|
|
28
|
+
},
|
|
29
|
+
"./styles/tokens.css": "./styles/tokens.css",
|
|
30
|
+
"./styles/tailwind-theme.css": "./styles/tailwind-theme.css",
|
|
31
|
+
"./styles/tailwind-sources.css": "./styles/tailwind-sources.css"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"styles",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
40
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/react": "^19.2.17",
|
|
44
|
+
"@types/react-dom": "^19.2.3",
|
|
45
|
+
"@vitejs/plugin-react": "^5.0.4",
|
|
46
|
+
"typescript": "^5.9.3",
|
|
47
|
+
"vite": "^7.1.7",
|
|
48
|
+
"vite-plugin-dts": "^4.5.4"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register this package for Tailwind v4 class detection.
|
|
3
|
+
* Import once from the app (paths are relative to this file inside the package):
|
|
4
|
+
*
|
|
5
|
+
* @import "@lifanh/quiet-paper/styles/tailwind-sources.css";
|
|
6
|
+
*/
|
|
7
|
+
/* Scan published JS so Tailwind v4 sees class names (Pattern A, Tailwind v4). */
|
|
8
|
+
@source "../dist";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind v4 @theme bridge — import after tokens.css in app global.css:
|
|
3
|
+
* @import "@lifanh/quiet-paper/styles/tokens.css" layer(theme);
|
|
4
|
+
* @import "@lifanh/quiet-paper/styles/tailwind-theme.css";
|
|
5
|
+
*/
|
|
6
|
+
@theme {
|
|
7
|
+
--color-bg: var(--color-bg);
|
|
8
|
+
--color-surface: var(--color-surface);
|
|
9
|
+
--color-text: var(--color-text);
|
|
10
|
+
--color-muted: var(--color-text-muted);
|
|
11
|
+
--color-border: var(--color-border);
|
|
12
|
+
--color-border-subtle: var(--color-border-subtle);
|
|
13
|
+
--color-accent: var(--color-accent);
|
|
14
|
+
--color-accent-hover: var(--color-accent-hover);
|
|
15
|
+
--color-error: var(--color-error);
|
|
16
|
+
--color-error-border: var(--color-error-border);
|
|
17
|
+
--color-error-surface: var(--color-error-surface);
|
|
18
|
+
|
|
19
|
+
--font-sans: var(--font-sans);
|
|
20
|
+
--font-ui: var(--font-ui);
|
|
21
|
+
--font-mono: var(--font-mono);
|
|
22
|
+
|
|
23
|
+
--spacing-1: var(--space-1);
|
|
24
|
+
--spacing-2: var(--space-2);
|
|
25
|
+
--spacing-3: var(--space-3);
|
|
26
|
+
--spacing-4: var(--space-4);
|
|
27
|
+
--spacing-5: var(--space-5);
|
|
28
|
+
--spacing-6: var(--space-6);
|
|
29
|
+
--spacing-8: var(--space-8);
|
|
30
|
+
--spacing-12: var(--space-12);
|
|
31
|
+
--spacing-16: var(--space-16);
|
|
32
|
+
|
|
33
|
+
--text-xs: var(--text-xs);
|
|
34
|
+
--text-sm: var(--text-sm);
|
|
35
|
+
--text-lg: var(--text-lg);
|
|
36
|
+
--text-xl: var(--text-xl);
|
|
37
|
+
--text-2xl: var(--text-2xl);
|
|
38
|
+
|
|
39
|
+
--leading-tight: var(--leading-tight);
|
|
40
|
+
--leading-normal: var(--leading-normal);
|
|
41
|
+
--leading-relaxed: var(--leading-relaxed);
|
|
42
|
+
|
|
43
|
+
--radius-sm: var(--radius-sm);
|
|
44
|
+
--radius-md: var(--radius-md);
|
|
45
|
+
|
|
46
|
+
--max-width-measure: var(--measure);
|
|
47
|
+
--max-width-measure-wide: var(--measure-wide);
|
|
48
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lifanh/quiet-paper — canonical design tokens
|
|
3
|
+
* Import in app global CSS before Tailwind @theme mapping.
|
|
4
|
+
*/
|
|
5
|
+
:root {
|
|
6
|
+
--color-bg: #f4f3ef;
|
|
7
|
+
--color-surface: #faf9f6;
|
|
8
|
+
--color-text: #1a1917;
|
|
9
|
+
--color-text-muted: #5c5a54;
|
|
10
|
+
--color-border: #ddd9d0;
|
|
11
|
+
--color-border-subtle: #ebe8e1;
|
|
12
|
+
|
|
13
|
+
--color-accent: #2a4f6e;
|
|
14
|
+
--color-accent-hover: #1f3d56;
|
|
15
|
+
--color-accent-muted: #6b8a9e;
|
|
16
|
+
|
|
17
|
+
/* Rust ink — enough red chroma for deutan/protan, still not alarm UI */
|
|
18
|
+
--color-error: #a33232;
|
|
19
|
+
--color-error-border: #c87868;
|
|
20
|
+
/* Salmon wash — clearly “marked” vs cream surface, not another gray */
|
|
21
|
+
--color-error-surface: #f8e8e4;
|
|
22
|
+
|
|
23
|
+
--space-1: 0.25rem;
|
|
24
|
+
--space-2: 0.5rem;
|
|
25
|
+
--space-3: 0.75rem;
|
|
26
|
+
--space-4: 1rem;
|
|
27
|
+
--space-5: 1.25rem;
|
|
28
|
+
--space-6: 1.5rem;
|
|
29
|
+
--space-8: 2rem;
|
|
30
|
+
--space-10: 2.5rem;
|
|
31
|
+
--space-12: 3rem;
|
|
32
|
+
--space-16: 4rem;
|
|
33
|
+
|
|
34
|
+
--font-sans:
|
|
35
|
+
"Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
|
|
36
|
+
--font-ui: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
37
|
+
--font-mono: ui-monospace, "SF Mono", Menlo, monospace;
|
|
38
|
+
|
|
39
|
+
--text-xs: 0.8125rem;
|
|
40
|
+
--text-sm: 0.875rem;
|
|
41
|
+
--text-base: 1rem;
|
|
42
|
+
--text-lg: 1.125rem;
|
|
43
|
+
--text-xl: 1.3125rem;
|
|
44
|
+
--text-2xl: 1.625rem;
|
|
45
|
+
--text-3xl: 2rem;
|
|
46
|
+
|
|
47
|
+
--leading-tight: 1.3;
|
|
48
|
+
--leading-normal: 1.55;
|
|
49
|
+
--leading-relaxed: 1.65;
|
|
50
|
+
|
|
51
|
+
--measure: 38rem;
|
|
52
|
+
--measure-wide: 48rem;
|
|
53
|
+
|
|
54
|
+
--radius-sm: 3px;
|
|
55
|
+
--radius-md: 6px;
|
|
56
|
+
|
|
57
|
+
--density-prose: 1;
|
|
58
|
+
--density-ui: 0.875;
|
|
59
|
+
}
|