@domicile-desktop/component-library 0.0.0-alpha-86d2d09c8a70 → 0.0.0-alpha-258d9aabe264

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 CHANGED
@@ -2,8 +2,8 @@
2
2
  "dependencies": {
3
3
  "@base-ui/react": "^1.8.0",
4
4
  "@cprussin/option-result": "^2.0.1",
5
- "@domicile-desktop/sdk": "0.0.0-alpha-86d2d09c8a70",
6
- "@domicile-desktop/system-apps": "0.0.0-alpha-86d2d09c8a70",
5
+ "@domicile-desktop/sdk": "0.0.0-alpha-258d9aabe264",
6
+ "@domicile-desktop/system-apps": "0.0.0-alpha-258d9aabe264",
7
7
  "@pandacss/dev": "^1.12.1",
8
8
  "@phosphor-icons/react": "^2.1.10"
9
9
  },
@@ -78,6 +78,8 @@
78
78
  "./FilePicker": "./src/FilePicker/FilePicker.tsx",
79
79
  "./file-request": "./src/FilePicker/file-request.ts",
80
80
  "./folders": "./src/FilePicker/folders.ts",
81
+ "./font": "./src/fonts/font.ts",
82
+ "./fonts/inter.css": "./src/fonts/inter.css",
81
83
  "./IconButton": "./src/IconButton/IconButton.tsx",
82
84
  "./Input": "./src/Input/Input.tsx",
83
85
  "./Kbd": "./src/Kbd/Kbd.tsx",
@@ -157,5 +159,5 @@
157
159
  "test:unit": "bun test"
158
160
  },
159
161
  "type": "module",
160
- "version": "0.0.0-alpha-86d2d09c8a70"
162
+ "version": "0.0.0-alpha-258d9aabe264"
161
163
  }
@@ -36,6 +36,11 @@ type CommonProps<V> = Partial<ControlVariant> & {
36
36
  disabled?: boolean | undefined;
37
37
  /** Shown in place of the list when `suggestions` is empty. */
38
38
  emptyMessage?: ReactNode | undefined;
39
+ /**
40
+ * A tinted field with no edge until hovered or focused, as in an address
41
+ * bar, which sits on a toolbar rather than on a page.
42
+ */
43
+ filled?: boolean | undefined;
39
44
  onSuggestionTaken?: ((value: V) => void) | undefined;
40
45
  onValueChange?: ((value: string) => void) | undefined;
41
46
  rounded?: boolean | undefined;
@@ -74,6 +79,7 @@ export const Autocomplete = <V,>({
74
79
  autoHighlight = false,
75
80
  disabled,
76
81
  emptyMessage,
82
+ filled = false,
77
83
  onSuggestionTaken,
78
84
  onValueChange,
79
85
  prefixButtons,
@@ -107,7 +113,7 @@ export const Autocomplete = <V,>({
107
113
  className={cx(
108
114
  control({ size }),
109
115
  wrapperBase,
110
- wrapperStyles({ rounded, size }),
116
+ wrapperStyles({ filled, rounded, size }),
111
117
  )}
112
118
  onMouseDown={focusControlOnMouseDown}
113
119
  ref={attach}
@@ -211,6 +217,15 @@ const wrapperStyles = cva({
211
217
  { css: { paddingInline: 11.5 }, rounded: true, size: "4xl" },
212
218
  ],
213
219
  variants: {
220
+ // `wrapperBase` still draws the hover and focus edges.
221
+ filled: {
222
+ false: {},
223
+ true: {
224
+ backgroundColor: "card",
225
+ borderColor: "transparent",
226
+ borderRadius: "lg",
227
+ },
228
+ },
214
229
  rounded: {
215
230
  true: { borderRadius: "full" },
216
231
  },
@@ -820,13 +820,16 @@ const sidebarStyles = flex({
820
820
 
821
821
  const placesStyles = flex({
822
822
  "& > button": {
823
+ // 13px, the desktop's text size, as the folder's rows.
824
+ fontSize: "0.8125rem",
823
825
  gap: 2.5,
824
826
  inlineSize: "100%",
825
827
  justifyContent: "flex-start",
826
828
  },
827
829
  "& > button[aria-current=location]": {
828
- backgroundColor: "color-mix(in oklab, {colors.accent} 16%, transparent)",
829
- color: "accent",
830
+ backgroundColor:
831
+ "color-mix(in oklab, {colors.foreground} 10%, transparent)",
832
+ color: "foreground",
830
833
  fontWeight: "semibold",
831
834
  },
832
835
  direction: "column",
@@ -883,7 +886,11 @@ const crumbSeparatorStyles = css({
883
886
  });
884
887
 
885
888
  const resultsStyles = css({
886
- backgroundColor: "color-mix(in oklab, {colors.background} 30%, transparent)",
889
+ // White in light mode, as a document is.
890
+ backgroundColor: {
891
+ _light: "color-mix(in oklab, {colors.white} 80%, transparent)",
892
+ base: "color-mix(in oklab, {colors.background} 30%, transparent)",
893
+ },
887
894
  border: "1px solid {colors.border}",
888
895
  borderRadius: "lg",
889
896
  flex: "1 1 auto",
@@ -902,7 +909,8 @@ const columnsStyles = grid({
902
909
  backgroundColor: "color-mix(in oklab, {colors.card} 70%, transparent)",
903
910
  color: "textTertiary",
904
911
  columnGap: 3,
905
- fontSize: "2xs",
912
+ // 11px, which has no scale token.
913
+ fontSize: "0.6875rem",
906
914
  fontWeight: "semibold",
907
915
  gridTemplateColumns: "minmax(0, 1fr) {spacing.32}",
908
916
  insetBlockStart: 0,
@@ -927,21 +935,14 @@ const rowStyles = grid({
927
935
  _hoverEnabled: {
928
936
  backgroundColor: "color-mix(in oklab, {colors.foreground} 5%, transparent)",
929
937
  },
930
- // The tile color shows the file kind. Tones that blend two hues mix in
931
- // OKLCH, since OKLab mixing passes through gray.
932
- "& [data-row-tile]": {
933
- backgroundColor: "color-mix(in oklab, var(--tone) 16%, transparent)",
934
- borderColor: "color-mix(in oklab, var(--tone) 26%, transparent)",
935
- color: "var(--tone)",
936
- },
938
+ "--tone": "{colors.muted}",
939
+ // A plain glyph, teal for a folder and gray for anything else, so folders
940
+ // stand out without a tile behind every row.
941
+ "& [data-row-tile]": { color: "var(--tone)" },
942
+ // A neutral wash: color is kept for focus and the main action.
937
943
  "&[data-highlighted]": {
938
- backgroundImage:
939
- "linear-gradient(to right, color-mix(in oklab, {colors.accent} 30%, transparent), color-mix(in oklab, {colors.accent} 8%, transparent))",
940
- },
941
- "&[data-highlighted] [data-row-tile]": {
942
- backgroundColor: "color-mix(in oklab, {colors.accent} 28%, transparent)",
943
- borderColor: "color-mix(in oklab, {colors.accent} 45%, transparent)",
944
- color: "accent",
944
+ backgroundColor:
945
+ "color-mix(in oklab, {colors.foreground} 10%, transparent)",
945
946
  },
946
947
  "&[data-marked] [data-row-tile]": {
947
948
  backgroundColor: "accent",
@@ -952,24 +953,8 @@ const rowStyles = grid({
952
953
  "&[data-replaced]": {
953
954
  backgroundColor: "color-mix(in oklab, {colors.warning} 14%, transparent)",
954
955
  },
955
- "&[data-tone=archive]": { "--tone": "{colors.warning}" },
956
- "&[data-tone=audio]": {
957
- "--tone": "color-mix(in oklch, {colors.warning} 50%, {colors.danger})",
958
- },
959
- "&[data-tone=code]": { "--tone": "{colors.success}" },
960
- "&[data-tone=document]": {
961
- "--tone": "color-mix(in oklab, {colors.accent} 45%, {colors.foreground})",
962
- },
963
956
  "&[data-tone=folder]": { "--tone": "{colors.accent}" },
964
- "&[data-tone=image]": {
965
- "--tone": "color-mix(in oklch, {colors.danger} 55%, {colors.accent})",
966
- },
967
- "&[data-tone=other]": { "--tone": "{colors.muted}" },
968
- "&[data-tone=parent]": { "--tone": "{colors.muted}", color: "muted" },
969
- "&[data-tone=pdf]": { "--tone": "{colors.danger}" },
970
- "&[data-tone=video]": {
971
- "--tone": "color-mix(in oklch, {colors.danger} 80%, {colors.warning})",
972
- },
957
+ "&[data-tone=parent]": { color: "muted" },
973
958
  alignItems: "center",
974
959
  borderRadius: "md",
975
960
  color: "foreground",
@@ -994,7 +979,8 @@ const rowTileStyles = css({
994
979
  });
995
980
 
996
981
  const rowNameStyles = css({
997
- fontSize: "sm",
982
+ // 13px, the desktop's text size, between the scale's 12px and 14px.
983
+ fontSize: "0.8125rem",
998
984
  fontWeight: "medium",
999
985
  overflow: "hidden",
1000
986
  textOverflow: "ellipsis",
@@ -17,6 +17,11 @@ export { SIZES, type Size } from "../control-sizes";
17
17
 
18
18
  type CommonProps = Partial<ControlVariant> & {
19
19
  clearable?: boolean | undefined;
20
+ /**
21
+ * No box: just the text and its icons, for a field that heads a panel. It
22
+ * draws no edge, fill or focus ring.
23
+ */
24
+ flush?: boolean | undefined;
20
25
  prefixIcon?: ReactNode | undefined;
21
26
  // Narrower than base-ui's `Ref<HTMLElement>` since this always renders an
22
27
  // `<input>`. Ref objects are invariant, so the wider type would need a cast.
@@ -36,6 +41,7 @@ type Props = ExtendProps<
36
41
 
37
42
  export const Input = ({
38
43
  clearable = false,
44
+ flush = false,
39
45
  prefixIcon,
40
46
  rounded = false,
41
47
  size = "md",
@@ -74,8 +80,9 @@ export const Input = ({
74
80
  className={cx(
75
81
  control({ size }),
76
82
  wrapperBase,
77
- wrapperStyles({ rounded, size }),
83
+ wrapperStyles({ flush, rounded, size }),
78
84
  )}
85
+ data-flush={flush ? "" : undefined}
79
86
  onMouseDown={focusControlOnMouseDown}
80
87
  style={controlSizingStyle({ width })}
81
88
  >
@@ -133,6 +140,20 @@ const wrapperStyles = cva({
133
140
  { css: { paddingInline: 11.5 }, rounded: true, size: "4xl" },
134
141
  ],
135
142
  variants: {
143
+ // `wrapperBase` and glass surfaces skip their edges and fills for
144
+ // `[data-flush]`.
145
+ // Under `[data-flush]`, to outrank `wrapperBase`'s own fill and edge.
146
+ flush: {
147
+ false: {},
148
+ true: {
149
+ "&[data-flush]": {
150
+ backgroundColor: "transparent",
151
+ borderColor: "transparent",
152
+ borderRadius: 0,
153
+ outlineStyle: "none",
154
+ },
155
+ },
156
+ },
136
157
  rounded: {
137
158
  true: { borderRadius: "full" },
138
159
  },
@@ -31,8 +31,8 @@ export const SURFACES = ["card", "glass"] as const;
31
31
  export type Surface = (typeof SURFACES)[number];
32
32
 
33
33
  /**
34
- * The popup's width. `lg` fits rows with several columns; `xl` fits a list
35
- * beside a preview.
34
+ * The popup's width. `lg` fits rows with several columns, or a compact list
35
+ * beside a preview; `xl` fits a roomy list beside a preview.
36
36
  */
37
37
  export const SIZES = ["md", "lg", "xl"] as const;
38
38
  export type Size = (typeof SIZES)[number];
@@ -68,6 +68,11 @@ type Props = ExtendProps<
68
68
  size?: Size | undefined;
69
69
  surface?: Surface | undefined;
70
70
  title?: ReactNode | undefined;
71
+ /**
72
+ * Whether `title` is for screen readers only, as when the popup's first
73
+ * control already says what it is for.
74
+ */
75
+ titleHidden?: boolean | undefined;
71
76
  trigger?: ReactElement | undefined;
72
77
  }
73
78
  >;
@@ -83,6 +88,7 @@ const ModalDialogComponent = ({
83
88
  size = "md",
84
89
  surface = "card",
85
90
  title,
91
+ titleHidden = false,
86
92
  trigger,
87
93
  ...rootProps
88
94
  }: Props) => {
@@ -119,7 +125,12 @@ const ModalDialogComponent = ({
119
125
  finalFocus={returnFocus}
120
126
  onKeyDown={moveFooterFocus}
121
127
  >
122
- {title !== undefined && (
128
+ {title !== undefined && titleHidden && (
129
+ <BaseDialog.Title className={hiddenTitleStyles}>
130
+ {title}
131
+ </BaseDialog.Title>
132
+ )}
133
+ {title !== undefined && !titleHidden && (
123
134
  <header
124
135
  className={headerStyles({ hasCloseButton: closeButton })}
125
136
  >
@@ -143,7 +154,7 @@ const ModalDialogComponent = ({
143
154
  className={bodyStyles({
144
155
  hasCloseButton: closeButton,
145
156
  hasFooter: footer !== undefined,
146
- hasTitle: title !== undefined,
157
+ hasTitle: title !== undefined && !titleHidden,
147
158
  })}
148
159
  >
149
160
  {children}
@@ -225,7 +236,7 @@ const popupStyles = flex({
225
236
  marginBlockStart: "8cqh",
226
237
  },
227
238
  "&[data-size=lg]": {
228
- inlineSize: "min({spacing.180}, 92cqw)",
239
+ inlineSize: "min({spacing.220}, 92cqw)",
229
240
  },
230
241
  "&[data-size=xl]": {
231
242
  inlineSize: "min({spacing.320}, 92cqw)",
@@ -241,14 +252,18 @@ const popupStyles = flex({
241
252
  position: "absolute",
242
253
  },
243
254
  backdropFilter: "blur({spacing.5}) saturate(180%)",
244
- backgroundColor: "color-mix(in oklab, {colors.card} 62%, transparent)",
255
+ // Near white in light mode: the light card through blur reads as gray.
256
+ backgroundColor: {
257
+ _light: "color-mix(in oklab, {colors.white} 86%, transparent)",
258
+ base: "color-mix(in oklab, {colors.card} 62%, transparent)",
259
+ },
245
260
  borderColor: "color-mix(in oklab, {colors.foreground} 16%, transparent)",
246
261
  },
247
262
  // Makes form controls translucent on glass. Controls mark their element
248
263
  // with `data-control`, so its parent is the wrapper that paints the
249
264
  // background. The descendant selector outranks the wrapper's own class
250
265
  // regardless of order (see `_control/wrapperBase.ts`).
251
- "&[data-surface=glass] :has(> [data-control])": {
266
+ "&[data-surface=glass] :has(> [data-control]):not([data-flush])": {
252
267
  backgroundColor:
253
268
  "color-mix(in oklab, {colors.background} 45%, transparent)",
254
269
  },
@@ -293,6 +308,8 @@ const titleStyles = css({
293
308
  margin: 0,
294
309
  });
295
310
 
311
+ const hiddenTitleStyles = css({ srOnly: true });
312
+
296
313
  const closeStyles = css({
297
314
  insetBlockStart: 3,
298
315
  insetInlineEnd: 3,
@@ -7,7 +7,8 @@ import { css } from "../../styled-system/css";
7
7
  * it, marked `data-control`. Selectors always target `[data-control]` so
8
8
  * suffix buttons can't affect the wrapper; a disabled Send button must not
9
9
  * gray out the textarea. Precedence is disabled, invalid, focused, hovered,
10
- * enforced by the `:not(...)` chains.
10
+ * enforced by the `:not(...)` chains. A `[data-flush]` wrapper (a flush
11
+ * `Input`) draws no hover or focus edge.
11
12
  *
12
13
  * The wrapper looks focused when the control has `:focus`,
13
14
  * `[data-popup-open]` (a Select popup holds focus), or `[data-active]`
@@ -31,7 +32,7 @@ export const wrapperBase = css({
31
32
  "&:has([data-control]:is(:focus, [data-popup-open], [data-active]))": {
32
33
  color: "foreground",
33
34
  },
34
- "&:has([data-control]:is(:focus, [data-popup-open], [data-active])):not(:has([data-control][data-invalid]))":
35
+ "&:has([data-control]:is(:focus, [data-popup-open], [data-active])):not(:has([data-control][data-invalid])):not([data-flush])":
35
36
  {
36
37
  borderColor: "accent",
37
38
  outlineColor: "accent",
@@ -65,7 +66,7 @@ export const wrapperBase = css({
65
66
  "&:has([data-control][data-resizing]) [data-resize-handle]": {
66
67
  color: "muted",
67
68
  },
68
- "&:is(:hover, :has([data-control][data-resizing])):not(:has([data-control]:disabled)):not(:has([data-control]:is(:focus, [data-popup-open], [data-active]))):not(:has([data-control][data-invalid]))":
69
+ "&:is(:hover, :has([data-control][data-resizing])):not(:has([data-control]:disabled)):not(:has([data-control]:is(:focus, [data-popup-open], [data-active]))):not(:has([data-control][data-invalid])):not([data-flush])":
69
70
  {
70
71
  borderColor: "muted",
71
72
  },
@@ -0,0 +1,93 @@
1
+ Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter) Inter-Italic[opsz,wght].ttf: Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter)
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ http://scripts.sil.org/OFL
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -0,0 +1,22 @@
1
+ /** The custom property the preset's `fonts.body` token reads first. */
2
+ const DESKTOP_FONT = "--desktop-font";
3
+
4
+ /**
5
+ * Draws the desktop's text in `family`, a face installed on the computer or
6
+ * Inter, which `inter.css` ships. A blank name draws Inter.
7
+ */
8
+ export const applyFont = (
9
+ family: string,
10
+ root: HTMLElement = document.documentElement,
11
+ ): void => {
12
+ const name = family.trim();
13
+ if (name === "") {
14
+ root.style.removeProperty(DESKTOP_FONT);
15
+ } else {
16
+ root.style.setProperty(DESKTOP_FONT, quoted(name));
17
+ }
18
+ };
19
+
20
+ /** `name` as a CSS string, so any name is one family. */
21
+ const quoted = (name: string): string =>
22
+ `"${name.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
@@ -0,0 +1,73 @@
1
+ /*
2
+ * Inter, the desktop's default face: variable weight, upright, every script
3
+ * Inter covers. Named `Inter`, so a desktop's `font` option of "Inter" draws
4
+ * these files, not an installed copy. From @fontsource-variable/inter 5.3.0;
5
+ * SIL Open Font License, see LICENSE-Inter.txt.
6
+ */
7
+ @font-face {
8
+ font-family: Inter;
9
+ font-style: normal;
10
+ font-display: block;
11
+ font-weight: 100 900;
12
+ src: url(./inter-cyrillic-ext-wght-normal.woff2) format("woff2-variations");
13
+ unicode-range:
14
+ U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
15
+ }
16
+
17
+ @font-face {
18
+ font-family: Inter;
19
+ font-style: normal;
20
+ font-display: block;
21
+ font-weight: 100 900;
22
+ src: url(./inter-cyrillic-wght-normal.woff2) format("woff2-variations");
23
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
24
+ }
25
+
26
+ @font-face {
27
+ font-family: Inter;
28
+ font-style: normal;
29
+ font-display: block;
30
+ font-weight: 100 900;
31
+ src: url(./inter-greek-ext-wght-normal.woff2) format("woff2-variations");
32
+ unicode-range: U+1F00-1FFF;
33
+ }
34
+
35
+ @font-face {
36
+ font-family: Inter;
37
+ font-style: normal;
38
+ font-display: block;
39
+ font-weight: 100 900;
40
+ src: url(./inter-greek-wght-normal.woff2) format("woff2-variations");
41
+ unicode-range:
42
+ U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
43
+ }
44
+
45
+ @font-face {
46
+ font-family: Inter;
47
+ font-style: normal;
48
+ font-display: block;
49
+ font-weight: 100 900;
50
+ src: url(./inter-vietnamese-wght-normal.woff2) format("woff2-variations");
51
+ unicode-range:
52
+ U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
53
+ }
54
+
55
+ @font-face {
56
+ font-family: Inter;
57
+ font-style: normal;
58
+ font-display: block;
59
+ font-weight: 100 900;
60
+ src: url(./inter-latin-ext-wght-normal.woff2) format("woff2-variations");
61
+ unicode-range:
62
+ U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
63
+ }
64
+
65
+ @font-face {
66
+ font-family: Inter;
67
+ font-style: normal;
68
+ font-display: block;
69
+ font-weight: 100 900;
70
+ src: url(./inter-latin-wght-normal.woff2) format("woff2-variations");
71
+ unicode-range:
72
+ U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
73
+ }
@@ -206,6 +206,7 @@ export const domicilePreset = definePreset({
206
206
  backgroundColor: "background",
207
207
  color: "foreground",
208
208
  colorScheme: "dark",
209
+ fontFamily: "body",
209
210
  },
210
211
  },
211
212
  name: "domicile",
@@ -567,6 +568,13 @@ export const domicilePreset = definePreset({
567
568
  outQuart: { value: "cubic-bezier(0.25, 1, 0.5, 1)" },
568
569
  },
569
570
  fonts: {
571
+ // The desktop's face: `--desktop-font`, which a shell sets from its
572
+ // options (see `applyFont`), then Inter, which `fonts/inter.css`
573
+ // ships, so text looks the same on every computer.
574
+ body: {
575
+ value:
576
+ "var(--desktop-font, Inter), Inter, ui-sans-serif, system-ui, sans-serif",
577
+ },
570
578
  mono: {
571
579
  value:
572
580
  "ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Monaco, Consolas, monospace",
package/src/vite-shell.ts CHANGED
@@ -77,6 +77,11 @@ export const shellBuild = ({
77
77
  options,
78
78
  }: ShellBuild) => ({
79
79
  build: {
80
+ // Fonts go in the module with the CSS that names them: a relative URL in
81
+ // that CSS would resolve against the document, not the asset. Other
82
+ // assets keep vite's size rule.
83
+ assetsInlineLimit: (file: string, _content: Buffer): true | undefined =>
84
+ file.endsWith(".woff2") ? true : undefined,
80
85
  // No content hash, so configs and packages can name the module path.
81
86
  //
82
87
  // Keep the entry's exports. Vite's app default drops them, which removes
@@ -1371,6 +1371,10 @@ const tokens = {
1371
1371
  "value": "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif",
1372
1372
  "variable": "var(--guQdxN)"
1373
1373
  },
1374
+ "fonts.body": {
1375
+ "value": "var(--desktop-font, Inter), Inter, ui-sans-serif, system-ui, sans-serif",
1376
+ "variable": "var(--EkLlm)"
1377
+ },
1374
1378
  "fonts.mono": {
1375
1379
  "value": "ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Monaco, Consolas, monospace",
1376
1380
  "variable": "var(--gxQaAd)"
@@ -31,7 +31,7 @@ export type DurationToken = "fastest" | "faster" | "fast" | "normal" | "slow" |
31
31
 
32
32
  export type EasingToken = "default" | "linear" | "in" | "out" | "in-out" | "emphasized" | "outBack" | "outQuart"
33
33
 
34
- export type FontToken = "sans" | "serif" | "mono"
34
+ export type FontToken = "sans" | "serif" | "body" | "mono"
35
35
 
36
36
  export type GradientToken = "scrimOverPhoto"
37
37