@wtfalch/design 0.11.0 → 0.13.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 +30 -0
- package/dist/components/RichText.d.ts +24 -0
- package/dist/components/RichText.js +69 -0
- package/dist/components/RichTextEditor.d.ts +32 -0
- package/dist/components/RichTextEditor.js +163 -0
- package/dist/components/Rows.d.ts +8 -1
- package/dist/components/Rows.js +3 -2
- package/dist/editor.d.ts +16 -0
- package/dist/editor.js +16 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +5 -0
- package/dist/rich-text/schema.d.ts +219 -0
- package/dist/rich-text/schema.js +88 -0
- package/dist/rich-text/value.d.ts +25 -0
- package/dist/rich-text/value.js +41 -0
- package/dist/rich-text.d.ts +11 -0
- package/dist/rich-text.js +11 -0
- package/dist/styles/index.css +126 -6
- package/dist/tf.css +126 -6
- package/dist/valet.css +126 -6
- package/package.json +44 -4
package/dist/valet.css
CHANGED
|
@@ -311,6 +311,7 @@
|
|
|
311
311
|
--text-2xs: var(--text-2xs);
|
|
312
312
|
--text-md: var(--text-md);
|
|
313
313
|
--font-weight-strong: var(--weight-strong);
|
|
314
|
+
--radius-DEFAULT: var(--radius);
|
|
314
315
|
--radius-pill: var(--radius-pill);
|
|
315
316
|
--color-text: var(--text);
|
|
316
317
|
--color-muted: var(--muted);
|
|
@@ -447,6 +448,9 @@
|
|
|
447
448
|
.min-h-0 {
|
|
448
449
|
min-height: 0px;
|
|
449
450
|
}
|
|
451
|
+
.min-h-\[calc\(var\(--space-15\)\*2\)\] {
|
|
452
|
+
min-height: calc(var(--space-15) * 2);
|
|
453
|
+
}
|
|
450
454
|
.min-h-screen {
|
|
451
455
|
min-height: 100vh;
|
|
452
456
|
}
|
|
@@ -587,6 +591,9 @@
|
|
|
587
591
|
.rounded {
|
|
588
592
|
border-radius: 0.25rem;
|
|
589
593
|
}
|
|
594
|
+
.rounded-\(--radius\) {
|
|
595
|
+
border-radius: var(--radius);
|
|
596
|
+
}
|
|
590
597
|
.rounded-full {
|
|
591
598
|
border-radius: calc(infinity * 1px);
|
|
592
599
|
}
|
|
@@ -736,6 +743,9 @@
|
|
|
736
743
|
.uppercase {
|
|
737
744
|
text-transform: uppercase;
|
|
738
745
|
}
|
|
746
|
+
.italic {
|
|
747
|
+
font-style: italic;
|
|
748
|
+
}
|
|
739
749
|
.tabular-nums {
|
|
740
750
|
--tw-numeric-spacing: tabular-nums;
|
|
741
751
|
font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
|
|
@@ -782,9 +792,16 @@
|
|
|
782
792
|
.surface-bg {
|
|
783
793
|
background: var(--bg);
|
|
784
794
|
}
|
|
795
|
+
.surface-control {
|
|
796
|
+
background: var(--control);
|
|
797
|
+
}
|
|
785
798
|
.surface-panel {
|
|
786
799
|
background: var(--panel);
|
|
787
800
|
}
|
|
801
|
+
.focus-within\:shadow-\(--focus-ring\):focus-within {
|
|
802
|
+
--tw-shadow: var(--focus-ring);
|
|
803
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
804
|
+
}
|
|
788
805
|
.disabled\:cursor-default:disabled {
|
|
789
806
|
cursor: default;
|
|
790
807
|
}
|
|
@@ -2814,8 +2831,10 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2814
2831
|
}
|
|
2815
2832
|
|
|
2816
2833
|
/* Only where there is something to pick. A row without `row-hit` is not a
|
|
2817
|
-
choice, and lighting it up on hover promises something that does not happen.
|
|
2818
|
-
|
|
2834
|
+
choice, and lighting it up on hover promises something that does not happen.
|
|
2835
|
+
Nor is a row whose hit is disabled: a waiting row lit up under its own
|
|
2836
|
+
`not-allowed` cursor, and `disabled` would have made that permanent. */
|
|
2837
|
+
.rows-pick > .rows-row:has(.row-hit:enabled):hover {
|
|
2819
2838
|
border-color: var(--accent);
|
|
2820
2839
|
background: var(--panel-2);
|
|
2821
2840
|
}
|
|
@@ -2867,9 +2886,16 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2867
2886
|
/* And nothing else can be chosen while it happens: picking a second model
|
|
2868
2887
|
mid-load queues a second load, which is how a machine ends up swapping two
|
|
2869
2888
|
sets of weights it cannot hold at once. */
|
|
2870
|
-
.rows-pick > .rows-row.is-waiting
|
|
2889
|
+
.rows-pick > .rows-row.is-waiting,
|
|
2890
|
+
.rows-pick > .rows-row.is-disabled { opacity: 0.5; }
|
|
2871
2891
|
|
|
2872
|
-
.rows-pick > .rows-row.is-waiting .row-hit
|
|
2892
|
+
.rows-pick > .rows-row.is-waiting .row-hit,
|
|
2893
|
+
.rows-pick > .rows-row.is-disabled .row-hit { cursor: not-allowed; }
|
|
2894
|
+
|
|
2895
|
+
/* Dimmed once. `.row-hit:disabled` dims the button as well, and under the
|
|
2896
|
+
row's own opacity the hint of a disabled row, the line saying why it cannot
|
|
2897
|
+
be chosen, came out at a quarter strength. */
|
|
2898
|
+
.rows-pick > .rows-row.is-disabled > .row-hit:disabled { opacity: 1; }
|
|
2873
2899
|
|
|
2874
2900
|
@media (prefers-reduced-motion: reduce) {
|
|
2875
2901
|
/* The border still says which one, without the breathing. */
|
|
@@ -2921,7 +2947,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2921
2947
|
margin-bottom: calc(var(--space-2) * -1);
|
|
2922
2948
|
}
|
|
2923
2949
|
|
|
2924
|
-
.rows-pick > .rows-row.is-picked:has(.row-hit):hover {
|
|
2950
|
+
.rows-pick > .rows-row.is-picked:has(.row-hit:enabled):hover {
|
|
2925
2951
|
background: color-mix(in srgb, var(--accent) 13%, var(--panel));
|
|
2926
2952
|
}
|
|
2927
2953
|
|
|
@@ -2980,7 +3006,7 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2980
3006
|
border-radius: var(--radius-sm);
|
|
2981
3007
|
}
|
|
2982
3008
|
|
|
2983
|
-
.row-hit:hover .truncate { color: var(--accent); }
|
|
3009
|
+
.row-hit:enabled:hover .truncate { color: var(--accent); }
|
|
2984
3010
|
|
|
2985
3011
|
.row-hit:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
2986
3012
|
}
|
|
@@ -4965,6 +4991,100 @@ body.splitting * { cursor: inherit !important; }
|
|
|
4965
4991
|
.ident-remove:hover { background: var(--panel); color: var(--text); }
|
|
4966
4992
|
.ident-remove:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
4967
4993
|
}
|
|
4994
|
+
|
|
4995
|
+
/* Prose and the surface it is written on, added in 0.8.0. Last, because
|
|
4996
|
+
`.prose` styles the elements the reset in `base.css` flattened and has to
|
|
4997
|
+
win over it, and because the editor's box sits over nothing. */
|
|
4998
|
+
/* ---- ./richtext.css ---- */
|
|
4999
|
+
@layer components {
|
|
5000
|
+
/* Prose: one class, and the elements it styles are ones nobody wrote.
|
|
5001
|
+
*
|
|
5002
|
+
* That is the whole reason a class exists here rather than utilities. Inside
|
|
5003
|
+
* the editor the paragraphs, headings and lists are ProseMirror's, built
|
|
5004
|
+
* from the document — there is no JSX to hang a utility on, so the choice is
|
|
5005
|
+
* descendant rules behind one class or an arbitrary variant per element,
|
|
5006
|
+
* which is the same selector spelled worse. `Markdown` has the same problem
|
|
5007
|
+
* for the same reason and answers it the same way, as `.md` in `base.css`.
|
|
5008
|
+
*
|
|
5009
|
+
* The editor and the view share the class on purpose. They have to look
|
|
5010
|
+
* identical and one set of rules is what guarantees it; two sets maintained
|
|
5011
|
+
* separately is precisely how a what-you-see-is-what-you-get editor stops
|
|
5012
|
+
* being one. Everything the components *do* render — the box, the toolbar,
|
|
5013
|
+
* the writing surface's own padding and focus ring — is utilities in the
|
|
5014
|
+
* component, where it belongs.
|
|
5015
|
+
*
|
|
5016
|
+
* `.rich-text` and not `.prose`: this package's classes ship unprefixed into
|
|
5017
|
+
* other people's stylesheets, and `.prose` is the one name in CSS most
|
|
5018
|
+
* likely to already be taken — Tailwind's typography plugin defines exactly
|
|
5019
|
+
* it. A consumer with that plugin and this package would have had two sets
|
|
5020
|
+
* of margins fighting, and it would have read as a theming bug.
|
|
5021
|
+
*
|
|
5022
|
+
* Every value here is a token. The vertical rhythm is `--space-*`, four
|
|
5023
|
+
* pixels a step and scaling with `--density`; the type steps are `--text-*`,
|
|
5024
|
+
* scaling with `--font-size`. A heading two sizes apart from its body at one
|
|
5025
|
+
* density is two sizes apart at every density.
|
|
5026
|
+
*/
|
|
5027
|
+
|
|
5028
|
+
.rich-text > * {
|
|
5029
|
+
margin: 0;
|
|
5030
|
+
}
|
|
5031
|
+
|
|
5032
|
+
.rich-text > * + * {
|
|
5033
|
+
margin-top: var(--space-3);
|
|
5034
|
+
}
|
|
5035
|
+
|
|
5036
|
+
.rich-text h2 {
|
|
5037
|
+
font-size: var(--text-lg);
|
|
5038
|
+
font-weight: var(--weight-strong);
|
|
5039
|
+
letter-spacing: -0.01em;
|
|
5040
|
+
}
|
|
5041
|
+
|
|
5042
|
+
.rich-text h3 {
|
|
5043
|
+
font-size: var(--text-md);
|
|
5044
|
+
font-weight: var(--weight-strong);
|
|
5045
|
+
}
|
|
5046
|
+
|
|
5047
|
+
/* A heading after prose needs more room above it than below: the space
|
|
5048
|
+
belongs to the section it opens, not to the paragraph it follows. */
|
|
5049
|
+
.rich-text > * + :is(h2, h3) {
|
|
5050
|
+
margin-top: var(--space-6);
|
|
5051
|
+
}
|
|
5052
|
+
|
|
5053
|
+
.rich-text :is(ul, ol) {
|
|
5054
|
+
padding-left: var(--space-5);
|
|
5055
|
+
}
|
|
5056
|
+
|
|
5057
|
+
.rich-text li + li {
|
|
5058
|
+
margin-top: var(--space-1);
|
|
5059
|
+
}
|
|
5060
|
+
|
|
5061
|
+
.rich-text a {
|
|
5062
|
+
color: var(--accent);
|
|
5063
|
+
text-decoration: underline;
|
|
5064
|
+
text-underline-offset: 2px;
|
|
5065
|
+
}
|
|
5066
|
+
|
|
5067
|
+
.rich-text strong {
|
|
5068
|
+
font-weight: var(--weight-strong);
|
|
5069
|
+
}
|
|
5070
|
+
|
|
5071
|
+
/* An empty editor says what goes in it. The placeholder extension puts the
|
|
5072
|
+
words on the empty node as `data-placeholder` and marks it, so `attr()` is
|
|
5073
|
+
reading an attribute of the element it is drawn on — which is the part a
|
|
5074
|
+
hand-rolled rule got wrong twice: a rule on the paragraph cannot reach an
|
|
5075
|
+
attribute on the box, and ProseMirror's empty paragraph holds a trailing
|
|
5076
|
+
`<br>` so `:empty` never matches it either. `pointer-events: none` so a
|
|
5077
|
+
click on the words still lands in the text.
|
|
5078
|
+
|
|
5079
|
+
Harmless on the view, which never produces the class. */
|
|
5080
|
+
.rich-text p.is-editor-empty:first-child::before {
|
|
5081
|
+
content: attr(data-placeholder);
|
|
5082
|
+
float: left;
|
|
5083
|
+
height: 0;
|
|
5084
|
+
color: var(--muted);
|
|
5085
|
+
pointer-events: none;
|
|
5086
|
+
}
|
|
5087
|
+
}
|
|
4968
5088
|
/* ---- valet: the themes ---- */
|
|
4969
5089
|
:root[data-theme='valet']{--bg:#f4f5f8;--panel:#ffffff;--panel-2:#eceef3;--border:#dcdfe7;--border-strong:#7b8597;--text:#171a21;--muted:#5b6474;--accent:#4f46e5;--accent-dim:#a9a4f0;--on-accent:#ffffff;--good:#1b7f4b;--warn:#8a5f0a;--bad:#bf3a31;--info:#0e6f8e;--app-bg:#f4f5f8;--shadow-1:0 4px 14px rgba(23, 26, 33, 0.08);--shadow-2:0 8px 24px rgba(23, 26, 33, 0.10);--shadow-3:0 12px 32px rgba(23, 26, 33, 0.12);--scrim:rgba(23, 26, 33, 0.32);color-scheme:light}
|
|
4970
5090
|
:root[data-theme='valet-night']{--bg:#0c0f14;--panel:#141820;--panel-2:#1b2029;--border:#262c37;--border-strong:#616b7d;--text:#e8eaf0;--muted:#9ba4b5;--accent:#8f88ff;--accent-dim:#3f3a8f;--on-accent:#0d0b2e;--good:#5fcb8f;--warn:#e2ae58;--bad:#f28b84;--info:#57c4e8;--app-bg:#0c0f14;--shadow-1:0 6px 20px rgba(0, 0, 0, 0.28);--shadow-2:0 8px 28px rgba(0, 0, 0, 0.34);--shadow-3:0 10px 34px rgba(0, 0, 0, 0.38);--scrim:rgba(0, 0, 0, 0.5);color-scheme:dark}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wtfalch/design",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "wtfalch's design system: themeable components on a fixed token vocabulary.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design-system",
|
|
@@ -48,7 +48,15 @@
|
|
|
48
48
|
"default": "./dist/themes/index.js"
|
|
49
49
|
},
|
|
50
50
|
"./tokens.css": "./dist/tokens.css",
|
|
51
|
-
"./styles.css": "./dist/styles/index.css"
|
|
51
|
+
"./styles.css": "./dist/styles/index.css",
|
|
52
|
+
"./editor": {
|
|
53
|
+
"types": "./dist/editor.d.ts",
|
|
54
|
+
"default": "./dist/editor.js"
|
|
55
|
+
},
|
|
56
|
+
"./rich-text": {
|
|
57
|
+
"types": "./dist/rich-text.d.ts",
|
|
58
|
+
"default": "./dist/rich-text.js"
|
|
59
|
+
}
|
|
52
60
|
},
|
|
53
61
|
"sideEffects": ["*.css"],
|
|
54
62
|
"engines": {
|
|
@@ -61,8 +69,14 @@
|
|
|
61
69
|
"test": "vitest run"
|
|
62
70
|
},
|
|
63
71
|
"peerDependencies": {
|
|
72
|
+
"@tiptap/extension-link": "^3.31.0",
|
|
73
|
+
"@tiptap/extensions": "^3.31.0",
|
|
74
|
+
"@tiptap/pm": "^3.31.0",
|
|
75
|
+
"@tiptap/react": "^3.31.0",
|
|
76
|
+
"@tiptap/starter-kit": "^3.31.0",
|
|
64
77
|
"react": "^19.0.0",
|
|
65
|
-
"react-dom": "^19.0.0"
|
|
78
|
+
"react-dom": "^19.0.0",
|
|
79
|
+
"zod": "^4.0.0"
|
|
66
80
|
},
|
|
67
81
|
"dependencies": {
|
|
68
82
|
"@radix-ui/react-slot": "^1.3.3",
|
|
@@ -72,6 +86,11 @@
|
|
|
72
86
|
},
|
|
73
87
|
"devDependencies": {
|
|
74
88
|
"@tailwindcss/cli": "^4.3.3",
|
|
89
|
+
"@tiptap/extension-link": "^3.31.3",
|
|
90
|
+
"@tiptap/extensions": "^3.31.3",
|
|
91
|
+
"@tiptap/pm": "^3.31.3",
|
|
92
|
+
"@tiptap/react": "^3.31.3",
|
|
93
|
+
"@tiptap/starter-kit": "^3.31.3",
|
|
75
94
|
"@types/node": "^22",
|
|
76
95
|
"@types/react": "^19",
|
|
77
96
|
"@types/react-dom": "^19",
|
|
@@ -79,6 +98,27 @@
|
|
|
79
98
|
"react-dom": "^19",
|
|
80
99
|
"tailwindcss": "^4.3.3",
|
|
81
100
|
"typescript": "^5.9.0",
|
|
82
|
-
"vitest": "^4.1.6"
|
|
101
|
+
"vitest": "^4.1.6",
|
|
102
|
+
"zod": "^4.0.0"
|
|
103
|
+
},
|
|
104
|
+
"peerDependenciesMeta": {
|
|
105
|
+
"@tiptap/extension-link": {
|
|
106
|
+
"optional": true
|
|
107
|
+
},
|
|
108
|
+
"@tiptap/extensions": {
|
|
109
|
+
"optional": true
|
|
110
|
+
},
|
|
111
|
+
"@tiptap/pm": {
|
|
112
|
+
"optional": true
|
|
113
|
+
},
|
|
114
|
+
"@tiptap/react": {
|
|
115
|
+
"optional": true
|
|
116
|
+
},
|
|
117
|
+
"@tiptap/starter-kit": {
|
|
118
|
+
"optional": true
|
|
119
|
+
},
|
|
120
|
+
"zod": {
|
|
121
|
+
"optional": true
|
|
122
|
+
}
|
|
83
123
|
}
|
|
84
124
|
}
|