@bitbaum/design-tokens 1.1.0 → 1.3.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 +140 -5
- package/composition.css +196 -0
- package/fonts/cormorant-garamond/OFL.txt +93 -0
- package/fonts/cormorant-garamond/cormorant-garamond-cyrillic-ext.woff2 +0 -0
- package/fonts/cormorant-garamond/cormorant-garamond-cyrillic.woff2 +0 -0
- package/fonts/cormorant-garamond/cormorant-garamond-italic-cyrillic-ext.woff2 +0 -0
- package/fonts/cormorant-garamond/cormorant-garamond-italic-cyrillic.woff2 +0 -0
- package/fonts/cormorant-garamond/cormorant-garamond-italic-latin-ext.woff2 +0 -0
- package/fonts/cormorant-garamond/cormorant-garamond-italic-latin.woff2 +0 -0
- package/fonts/cormorant-garamond/cormorant-garamond-italic-vietnamese.woff2 +0 -0
- package/fonts/cormorant-garamond/cormorant-garamond-latin-ext.woff2 +0 -0
- package/fonts/cormorant-garamond/cormorant-garamond-latin.woff2 +0 -0
- package/fonts/cormorant-garamond/cormorant-garamond-vietnamese.woff2 +0 -0
- package/fonts/dm-sans/OFL.txt +93 -0
- package/fonts/dm-sans/dm-sans-latin-ext.woff2 +0 -0
- package/fonts/dm-sans/dm-sans-latin.woff2 +0 -0
- package/fonts/faces.json +612 -0
- package/fonts/fraunces/OFL.txt +93 -0
- package/fonts/fraunces/fraunces-italic-latin-ext.woff2 +0 -0
- package/fonts/fraunces/fraunces-italic-latin.woff2 +0 -0
- package/fonts/fraunces/fraunces-italic-vietnamese.woff2 +0 -0
- package/fonts/fraunces/fraunces-latin-ext.woff2 +0 -0
- package/fonts/fraunces/fraunces-latin.woff2 +0 -0
- package/fonts/fraunces/fraunces-vietnamese.woff2 +0 -0
- package/fonts/geist/OFL.txt +93 -0
- package/fonts/geist/geist-cyrillic-ext.woff2 +0 -0
- package/fonts/geist/geist-cyrillic.woff2 +0 -0
- package/fonts/geist/geist-latin-ext.woff2 +0 -0
- package/fonts/geist/geist-latin.woff2 +0 -0
- package/fonts/geist/geist-vietnamese.woff2 +0 -0
- package/fonts/geist-mono/OFL.txt +93 -0
- package/fonts/geist-mono/geist-mono-cyrillic-ext.woff2 +0 -0
- package/fonts/geist-mono/geist-mono-cyrillic.woff2 +0 -0
- package/fonts/geist-mono/geist-mono-latin-ext.woff2 +0 -0
- package/fonts/geist-mono/geist-mono-latin.woff2 +0 -0
- package/fonts/geist-mono/geist-mono-symbols2.woff2 +0 -0
- package/fonts/geist-mono/geist-mono-vietnamese.woff2 +0 -0
- package/fonts/ibm-plex-mono/OFL.txt +93 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-400-cyrillic-ext.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-400-cyrillic.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-400-latin-ext.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-400-latin.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-400-vietnamese.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-500-cyrillic-ext.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-500-cyrillic.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-500-latin-ext.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-500-latin.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-500-vietnamese.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-600-cyrillic-ext.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-600-cyrillic.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-600-latin-ext.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-600-latin.woff2 +0 -0
- package/fonts/ibm-plex-mono/ibm-plex-mono-600-vietnamese.woff2 +0 -0
- package/fonts/inter/OFL.txt +93 -0
- package/fonts/inter/inter-cyrillic-ext.woff2 +0 -0
- package/fonts/inter/inter-cyrillic.woff2 +0 -0
- package/fonts/inter/inter-greek-ext.woff2 +0 -0
- package/fonts/inter/inter-greek.woff2 +0 -0
- package/fonts/inter/inter-latin-ext.woff2 +0 -0
- package/fonts/inter/inter-latin.woff2 +0 -0
- package/fonts/inter/inter-vietnamese.woff2 +0 -0
- package/fonts/jetbrains-mono/OFL.txt +93 -0
- package/fonts/jetbrains-mono/jetbrains-mono-cyrillic-ext.woff2 +0 -0
- package/fonts/jetbrains-mono/jetbrains-mono-cyrillic.woff2 +0 -0
- package/fonts/jetbrains-mono/jetbrains-mono-greek.woff2 +0 -0
- package/fonts/jetbrains-mono/jetbrains-mono-latin-ext.woff2 +0 -0
- package/fonts/jetbrains-mono/jetbrains-mono-latin.woff2 +0 -0
- package/fonts/jetbrains-mono/jetbrains-mono-vietnamese.woff2 +0 -0
- package/fonts/montserrat/OFL.txt +93 -0
- package/fonts/montserrat/montserrat-cyrillic-ext.woff2 +0 -0
- package/fonts/montserrat/montserrat-cyrillic.woff2 +0 -0
- package/fonts/montserrat/montserrat-latin-ext.woff2 +0 -0
- package/fonts/montserrat/montserrat-latin.woff2 +0 -0
- package/fonts/montserrat/montserrat-vietnamese.woff2 +0 -0
- package/fonts/space-grotesk/OFL.txt +93 -0
- package/fonts/space-grotesk/space-grotesk-latin-ext.woff2 +0 -0
- package/fonts/space-grotesk/space-grotesk-latin.woff2 +0 -0
- package/fonts/space-grotesk/space-grotesk-vietnamese.woff2 +0 -0
- package/next-fonts.mjs +221 -0
- package/package.json +23 -13
- package/tailwind-preset.js +1 -1
- package/tokens.css +33 -3
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2024 The Montserrat.Git Project Authors (https://github.com/JulietaUla/Montserrat.git)
|
|
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
|
+
https://openfontlicense.org
|
|
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.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2020 The Space Grotesk Project Authors (https://github.com/floriankarsten/space-grotesk)
|
|
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.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/next-fonts.mjs
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Write an app's `next/font/local` module for faces this package ships.
|
|
3
|
+
//
|
|
4
|
+
// design-tokens-next-fonts <out-file> "<export>=<face>[;key=value]..." ... [--check]
|
|
5
|
+
//
|
|
6
|
+
// keys: weight=500,700 weights to declare (default: the face's whole range)
|
|
7
|
+
// style=normal,italic styles to declare (default: normal)
|
|
8
|
+
// variable=--font-x the CSS variable the export sets
|
|
9
|
+
// subsets=latin,... subsets to declare (default: every subset shipped)
|
|
10
|
+
// preload=latin,... subsets to preload (default: latin; "none" for none)
|
|
11
|
+
// display=swap font-display (default: swap)
|
|
12
|
+
//
|
|
13
|
+
// --check exit 1 if <out-file> is not what this command would write
|
|
14
|
+
//
|
|
15
|
+
// WHY A GENERATOR
|
|
16
|
+
//
|
|
17
|
+
// `next/font/google` downloads its faces at build time, so a build depends on
|
|
18
|
+
// Google answering; these faces are committed in fonts/ instead. `next/font`
|
|
19
|
+
// accepts only literal options in the calling module, so no helper can call
|
|
20
|
+
// `localFont` for an app. And one call can carry only one `unicode-range`,
|
|
21
|
+
// while Google splits a face into subsets (latin, latin-ext, cyrillic …) that
|
|
22
|
+
// a page downloads only when its text needs them. Keeping that split means one
|
|
23
|
+
// call per subset, all naming the same family — the same @font-face rules
|
|
24
|
+
// `next/font/google` produced: the same files, weights, styles, ranges and
|
|
25
|
+
// preloads. That is long and mechanical, so it is written by this command from
|
|
26
|
+
// fonts/faces.json and checked with --check, not typed by hand.
|
|
27
|
+
//
|
|
28
|
+
// The family is the export's name, not "Inter": Turbopack names the family in
|
|
29
|
+
// `.variable` after the const and ignores a `font-family` declaration there
|
|
30
|
+
// (webpack honours it), so the export carries no declaration and every other
|
|
31
|
+
// subset declares the export's name. Both bundlers then agree. The fallback
|
|
32
|
+
// face is measured from the file (next/font/local) rather than taken from
|
|
33
|
+
// Google's table, so it differs by about one percent; it shows only until the
|
|
34
|
+
// face has loaded.
|
|
35
|
+
//
|
|
36
|
+
// The export named in the spec is the call for the first preloaded subset
|
|
37
|
+
// (latin): it carries `.variable`, `.className` and `.style`. The other
|
|
38
|
+
// subsets are exported as `<export><Subset>` because next/font requires every
|
|
39
|
+
// call to be assigned to a module-level const; nothing needs to read them.
|
|
40
|
+
import { existsSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
|
41
|
+
import { createRequire } from 'node:module';
|
|
42
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
43
|
+
import { fileURLToPath } from 'node:url';
|
|
44
|
+
|
|
45
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
46
|
+
const PKG = '@bitbaum/design-tokens';
|
|
47
|
+
export const faces = JSON.parse(readFileSync(join(here, 'fonts', 'faces.json'), 'utf8'));
|
|
48
|
+
|
|
49
|
+
/** "sans=inter;weight=500,700;variable=--font-inter" → a spec object. */
|
|
50
|
+
export function parseSpec(text) {
|
|
51
|
+
const [head, ...pairs] = text.split(';').map((s) => s.trim());
|
|
52
|
+
const [name, face] = head.split('=');
|
|
53
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(name ?? ''))
|
|
54
|
+
throw new Error(`"${text}": the export name must be an identifier`);
|
|
55
|
+
// The export's name becomes the font-family name, so it must not be a CSS generic family.
|
|
56
|
+
if (
|
|
57
|
+
/^(serif|sans-serif|monospace|cursive|fantasy|system-ui|math|emoji|fangsong|ui-\w+|inherit|initial|unset|default)$/i.test(
|
|
58
|
+
name,
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
throw new Error(
|
|
62
|
+
`"${text}": "${name}" is a CSS keyword; it names the font family, so pick another`,
|
|
63
|
+
);
|
|
64
|
+
if (!faces[face])
|
|
65
|
+
throw new Error(`"${text}": no face "${face}"; shipped: ${Object.keys(faces).join(', ')}`);
|
|
66
|
+
const spec = { name, face };
|
|
67
|
+
for (const pair of pairs) {
|
|
68
|
+
const [key, value] = pair.split('=');
|
|
69
|
+
if (!['weight', 'style', 'variable', 'subsets', 'preload', 'display'].includes(key))
|
|
70
|
+
throw new Error(`"${text}": unknown key "${key}"`);
|
|
71
|
+
spec[key] = value;
|
|
72
|
+
}
|
|
73
|
+
return spec;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const list = (v) =>
|
|
77
|
+
v
|
|
78
|
+
? v
|
|
79
|
+
.split(',')
|
|
80
|
+
.map((s) => s.trim())
|
|
81
|
+
.filter(Boolean)
|
|
82
|
+
: undefined;
|
|
83
|
+
const camel = (s) => s.replace(/(^|-)(\w)/g, (_, __, c) => c.toUpperCase());
|
|
84
|
+
|
|
85
|
+
/** The localFont calls for one spec, as plain data. */
|
|
86
|
+
export function calls(spec) {
|
|
87
|
+
const face = faces[spec.face];
|
|
88
|
+
const styles = list(spec.style) ?? ['normal'];
|
|
89
|
+
const weights = list(spec.weight);
|
|
90
|
+
const preload = spec.preload === 'none' ? [] : (list(spec.preload) ?? ['latin']);
|
|
91
|
+
const shipped = [...new Set(face.files.map((f) => f.subset))];
|
|
92
|
+
const subsets = list(spec.subsets) ?? shipped;
|
|
93
|
+
for (const s of [...subsets, ...preload])
|
|
94
|
+
if (!shipped.includes(s))
|
|
95
|
+
throw new Error(`${spec.face} has no subset "${s}"; shipped: ${shipped.join(', ')}`);
|
|
96
|
+
// Google's order is kept, because it decides which file wins a character two
|
|
97
|
+
// subsets both list. The export itself is the first preloaded subset (else
|
|
98
|
+
// the first), measured for the fallback metrics as next/font/google measures
|
|
99
|
+
// its preloaded latin file.
|
|
100
|
+
const ordered = shipped.filter((s) => subsets.includes(s));
|
|
101
|
+
const primary = ordered.find((s) => preload.includes(s)) ?? ordered[0];
|
|
102
|
+
return ordered.map((subset) => {
|
|
103
|
+
const isPrimary = subset === primary;
|
|
104
|
+
const src = [];
|
|
105
|
+
for (const style of styles) {
|
|
106
|
+
const files = face.files.filter((f) => f.subset === subset && f.style === style);
|
|
107
|
+
if (!files.length) throw new Error(`${spec.face} has no ${style} ${subset} file`);
|
|
108
|
+
if (face.variable) {
|
|
109
|
+
for (const weight of weights ?? [files[0].weight])
|
|
110
|
+
src.push({ file: files[0].file, weight, style });
|
|
111
|
+
} else {
|
|
112
|
+
for (const weight of weights ?? files.map((f) => f.weight)) {
|
|
113
|
+
const file = files.find((f) => f.weight === weight);
|
|
114
|
+
if (!file)
|
|
115
|
+
throw new Error(
|
|
116
|
+
`${spec.face} has no ${style} ${weight} file; shipped: ${files.map((f) => f.weight)}`,
|
|
117
|
+
);
|
|
118
|
+
src.push({ file: file.file, weight, style });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
name: isPrimary ? spec.name : spec.name + camel(subset),
|
|
124
|
+
primary: isPrimary,
|
|
125
|
+
src,
|
|
126
|
+
variable: isPrimary ? spec.variable : undefined,
|
|
127
|
+
display: spec.display ?? 'swap',
|
|
128
|
+
preload: preload.includes(subset),
|
|
129
|
+
adjustFontFallback: isPrimary
|
|
130
|
+
? face.category === 'serif'
|
|
131
|
+
? 'Times New Roman'
|
|
132
|
+
: undefined
|
|
133
|
+
: false,
|
|
134
|
+
family: spec.name,
|
|
135
|
+
unicodeRange: face.files.find((f) => f.subset === subset).unicodeRange,
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Where the package sits as seen from `dir`: node_modules/@bitbaum/design-tokens, not its realpath. */
|
|
141
|
+
export function packageDir(dir) {
|
|
142
|
+
for (let d = resolve(dir); ; d = dirname(d)) {
|
|
143
|
+
const candidate = join(d, 'node_modules', ...PKG.split('/'));
|
|
144
|
+
if (existsSync(join(candidate, 'package.json'))) return candidate;
|
|
145
|
+
if (dirname(d) === d) throw new Error(`${PKG} is not installed above ${dir}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function render(out, specs, argv) {
|
|
150
|
+
const base = relative(dirname(resolve(out)), packageDir(dirname(resolve(out))))
|
|
151
|
+
.split(sep)
|
|
152
|
+
.join('/');
|
|
153
|
+
const q = JSON.stringify;
|
|
154
|
+
const lines = [
|
|
155
|
+
'// Generated by @bitbaum/design-tokens. Do not edit; to change it, run:',
|
|
156
|
+
`// pnpm exec design-tokens-next-fonts ${argv.map((a) => (/^[\w./@-]+$/.test(a) ? a : `'${a}'`)).join(' ')}`,
|
|
157
|
+
'// The faces ship in the package (fonts/faces.json): no build fetches fonts.',
|
|
158
|
+
'import localFont from "next/font/local";',
|
|
159
|
+
];
|
|
160
|
+
for (const spec of specs) {
|
|
161
|
+
for (const c of calls(spec)) {
|
|
162
|
+
const opts = [
|
|
163
|
+
`src: [${c.src.map((s) => `{ path: ${q(`${base}/fonts/${s.file}`)}, weight: ${q(s.weight)}, style: ${q(s.style)} }`).join(', ')}]`,
|
|
164
|
+
c.variable ? `variable: ${q(c.variable)}` : null,
|
|
165
|
+
`display: ${q(c.display)}`,
|
|
166
|
+
c.preload ? null : 'preload: false',
|
|
167
|
+
c.adjustFontFallback === false
|
|
168
|
+
? 'adjustFontFallback: false'
|
|
169
|
+
: c.adjustFontFallback
|
|
170
|
+
? `adjustFontFallback: ${q(c.adjustFontFallback)}`
|
|
171
|
+
: null,
|
|
172
|
+
`declarations: [${c.primary ? '' : `{ prop: "font-family", value: ${q(`'${c.family}'`)} }, `}{ prop: "unicode-range", value: ${q(c.unicodeRange)} }]`,
|
|
173
|
+
].filter(Boolean);
|
|
174
|
+
lines.push('', `export const ${c.name} = localFont({ ${opts.join(', ')} });`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return lines.join('\n') + '\n';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Format with the app's own prettier and config, when it has one, so its format check passes. */
|
|
181
|
+
async function format(out, code) {
|
|
182
|
+
try {
|
|
183
|
+
const require = createRequire(resolve(out));
|
|
184
|
+
const mod = await import(require.resolve('prettier'));
|
|
185
|
+
const prettier = mod.default ?? mod;
|
|
186
|
+
const config = (await prettier.resolveConfig(resolve(out))) ?? {};
|
|
187
|
+
return await prettier.format(code, { ...config, filepath: resolve(out) });
|
|
188
|
+
} catch {
|
|
189
|
+
return code;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function main(argv) {
|
|
194
|
+
const check = argv.includes('--check');
|
|
195
|
+
const args = argv.filter((a) => a !== '--check');
|
|
196
|
+
const [out, ...specTexts] = args;
|
|
197
|
+
if (!out || !specTexts.length) {
|
|
198
|
+
console.error(
|
|
199
|
+
'usage: design-tokens-next-fonts <out-file> "<export>=<face>[;key=value]..." ... [--check]',
|
|
200
|
+
);
|
|
201
|
+
process.exit(2);
|
|
202
|
+
}
|
|
203
|
+
const code = await format(out, render(out, specTexts.map(parseSpec), args));
|
|
204
|
+
if (check) {
|
|
205
|
+
const current = existsSync(out) ? readFileSync(out, 'utf8') : '';
|
|
206
|
+
if (current !== code) {
|
|
207
|
+
console.error(
|
|
208
|
+
`${out} is not what design-tokens-next-fonts writes; re-run the command in its first lines.`,
|
|
209
|
+
);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
writeFileSync(out, code);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (
|
|
218
|
+
process.argv[1] &&
|
|
219
|
+
realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))
|
|
220
|
+
)
|
|
221
|
+
await main(process.argv.slice(2));
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitbaum/design-tokens",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"packageManager": "pnpm@11.25.0",
|
|
4
5
|
"engines": {
|
|
5
6
|
"node": ">=20"
|
|
6
7
|
},
|
|
@@ -8,7 +9,7 @@
|
|
|
8
9
|
"access": "public",
|
|
9
10
|
"provenance": true
|
|
10
11
|
},
|
|
11
|
-
"description": "Design SSOT for OrangeCat,
|
|
12
|
+
"description": "Design SSOT for OrangeCat, Loki and Solon — tokens, self-hosted faces, the Tailwind preset that maps them, and next/font/local calls for every face the fleet's apps use.",
|
|
12
13
|
"license": "MIT",
|
|
13
14
|
"repository": {
|
|
14
15
|
"type": "git",
|
|
@@ -16,31 +17,40 @@
|
|
|
16
17
|
},
|
|
17
18
|
"files": [
|
|
18
19
|
"tokens.css",
|
|
20
|
+
"composition.css",
|
|
19
21
|
"tailwind-preset.js",
|
|
22
|
+
"next-fonts.mjs",
|
|
20
23
|
"fonts"
|
|
21
24
|
],
|
|
22
25
|
"exports": {
|
|
23
26
|
"./tokens.css": "./tokens.css",
|
|
27
|
+
"./composition.css": "./composition.css",
|
|
24
28
|
"./tailwind-preset": "./tailwind-preset.js",
|
|
25
|
-
"./fonts/*": "./fonts/*"
|
|
29
|
+
"./fonts/*": "./fonts/*",
|
|
30
|
+
"./next-fonts": "./next-fonts.mjs",
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"bin": {
|
|
34
|
+
"design-tokens-next-fonts": "./next-fonts.mjs"
|
|
26
35
|
},
|
|
27
36
|
"sideEffects": [
|
|
28
37
|
"*.css"
|
|
29
38
|
],
|
|
30
|
-
"devDependencies": {
|
|
31
|
-
"@eslint/js": "^10.0.1",
|
|
32
|
-
"@types/node": "^26.4.0",
|
|
33
|
-
"eslint": "^10.9.1",
|
|
34
|
-
"globals": "^17.12.0",
|
|
35
|
-
"prettier": "3.9.6",
|
|
36
|
-
"typescript": "^6.0.3"
|
|
37
|
-
},
|
|
38
39
|
"scripts": {
|
|
39
40
|
"lint": "eslint .",
|
|
40
41
|
"typecheck": "tsc --noEmit",
|
|
41
42
|
"test": "node --test test/*.test.js",
|
|
42
43
|
"verify": "pnpm run format:check && pnpm run lint && pnpm run typecheck && pnpm test",
|
|
43
44
|
"format": "prettier --write .",
|
|
44
|
-
"format:check": "prettier --check ."
|
|
45
|
+
"format:check": "prettier --check .",
|
|
46
|
+
"fonts:fetch": "node scripts/fetch-fonts.mjs"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@eslint/js": "^10.0.1",
|
|
50
|
+
"@types/node": "^26.6.5",
|
|
51
|
+
"eslint": "^10.13.0",
|
|
52
|
+
"globals": "^17.13.0",
|
|
53
|
+
"prettier": "3.9.10",
|
|
54
|
+
"typescript": "^6.0.3"
|
|
45
55
|
}
|
|
46
|
-
}
|
|
56
|
+
}
|
package/tailwind-preset.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* content: [...],
|
|
12
12
|
* }
|
|
13
13
|
*
|
|
14
|
-
* Tailwind v4 apps (
|
|
14
|
+
* Tailwind v4 apps (Loki) consume tokens.css directly via @theme instead;
|
|
15
15
|
* the variable names are identical, so the two paths stay in sync.
|
|
16
16
|
*/
|
|
17
17
|
|
package/tokens.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @bitbaum/design-tokens — the design SSOT for OrangeCat,
|
|
2
|
+
* @bitbaum/design-tokens — the design SSOT for OrangeCat, Loki and Solon.
|
|
3
3
|
*
|
|
4
4
|
* ┌──────────────────────────────────────────────────────────────────────────┐
|
|
5
5
|
* │ TO RETHEME THE ENTIRE STACK, EDIT THE "KNOBS" BLOCK BELOW AND NOTHING │
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
*
|
|
132
132
|
* White on #ff5c00 is 3.10:1 — below the 4.5 AA floor — so every primary
|
|
133
133
|
* CTA in the fleet was failing at once: Solon's "Open the Dashboard",
|
|
134
|
-
* OrangeCat's "Get Started",
|
|
134
|
+
* OrangeCat's "Get Started", Loki's "Launch agent". Measured, not
|
|
135
135
|
* guessed. Near-black on the same orange is 6.10:1.
|
|
136
136
|
*
|
|
137
137
|
* The orange is deliberately NOT darkened to rescue white text: it would
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
/* ── LIGHT THEME ────────────────────────────────────────────────────────
|
|
156
156
|
:root is light and `.dark` overrides it — the same mechanism OrangeCat
|
|
157
157
|
uses, so an app can toggle themes without translating anything. An app
|
|
158
|
-
that is dark-only (Solon,
|
|
158
|
+
that is dark-only (Solon, Loki's public surface) pins it by putting
|
|
159
159
|
`class="dark"` on <html> and never removing it. */
|
|
160
160
|
|
|
161
161
|
/* Surfaces — page < base < raised < overlay < modal < drawer */
|
|
@@ -242,6 +242,32 @@
|
|
|
242
242
|
/* Bitcoin's own orange. Not a brand colour — it means "this is Bitcoin", so
|
|
243
243
|
it is deliberately NOT --public-accent and must not be used for actions. */
|
|
244
244
|
--bitcoin-orange: #f7931a;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* — Data maps ——————————————————————————————————————————————————————————
|
|
248
|
+
* One sequential scale for every choropleth (Solon's Places map first):
|
|
249
|
+
* viridis, dark to light, so it survives every colour-vision deficiency and
|
|
250
|
+
* reads in greyscale — its luminance rises at every step (pinned in
|
|
251
|
+
* test/contrast.test.js). Colour is never the only carrier: each map has a
|
|
252
|
+
* list with the same numbers, and a legend.
|
|
253
|
+
*
|
|
254
|
+
* Hatching marks an area more than one authority claims. A map takes no
|
|
255
|
+
* side, so a contested area gets the pattern, never a colour of its own:
|
|
256
|
+
* lines of --map-hatch-width every --map-hatch-gap at --map-hatch-angle, in
|
|
257
|
+
* the text colour, over whatever the scale says.
|
|
258
|
+
*/
|
|
259
|
+
--map-scale-1: #46327e;
|
|
260
|
+
--map-scale-2: #365c8d;
|
|
261
|
+
--map-scale-3: #277f8e;
|
|
262
|
+
--map-scale-4: #1fa187;
|
|
263
|
+
--map-scale-5: #4ac16d;
|
|
264
|
+
--map-scale-6: #a0da39;
|
|
265
|
+
--map-scale-7: #fde725;
|
|
266
|
+
--map-no-data: 0 0% 85%;
|
|
267
|
+
--map-boundary: 0 0% 45%;
|
|
268
|
+
--map-hatch-angle: 45deg;
|
|
269
|
+
--map-hatch-gap: 6px;
|
|
270
|
+
--map-hatch-width: 1.5px;
|
|
245
271
|
}
|
|
246
272
|
|
|
247
273
|
/* ══════════════════════════════════════════════════════════════════════════
|
|
@@ -273,6 +299,10 @@
|
|
|
273
299
|
--border-subtle: 0 0% 13%;
|
|
274
300
|
--border-default: 0 0% 20%;
|
|
275
301
|
--border-strong: 0 0% 24%;
|
|
302
|
+
|
|
303
|
+
/* Data maps: no-data and boundaries sit between the page and the scale */
|
|
304
|
+
--map-no-data: 0 0% 20%;
|
|
305
|
+
--map-boundary: 0 0% 40%;
|
|
276
306
|
--border-interactive: 0 0% 60%;
|
|
277
307
|
|
|
278
308
|
/* Accent */
|