@obosbbl/grunnmuren-tailwind 2.0.0-canary.1 → 2.0.0-canary.3

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 CHANGED
@@ -29,7 +29,7 @@ module.exports = {
29
29
  './src/app/**/*.{js,ts,jsx,tsx,}',
30
30
 
31
31
  // If you're using Grunnmuren's React components
32
- './node_modules/@obosbbl/grunnmuren-react/dist/**/*.mjs',
32
+ './node_modules/@obosbbl/grunnmuren-react/dist/**/*.{mjs,js}',
33
33
  ],
34
34
  };
35
35
  ```
@@ -57,19 +57,39 @@ module.exports = {
57
57
  };
58
58
  ```
59
59
 
60
+ ## Fonts
61
+
62
+ Fonts are automatically loaded from OBOS' CDN, so you don't have to host the font files yourself.
63
+
64
+ If you use [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), you have to allow `https://www.obos.no` as a [font-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/font-src), otherwise the fonts will be blocked from loading.
65
+
66
+ The preset includes a (local) fallback font to prevent [CLS](https://web.dev/articles/cls) while the fonts are loading. This is similar to the [font optimization in Next](https://nextjs.org/docs/app/building-your-application/optimizing/fonts). This is enabled by default, but can be disabled with the `includeFontFallback` option.
67
+
68
+ The fallback font metrics is generated with a script that can be run with `pnpm font-fallback` (requires [Bun](https://bun.sh/)). If the fonts are changed, the script must be rerun and the resulting file commited.
69
+
60
70
  ## Migrating from v1?
61
71
 
62
72
  To ease the transition from v1 to v2 of Grunnmuren, it is possible to configure the preset to be (partially) compatible with v1. This allows you to use v2 of the Tailwind preset with v1 of the React components, and upgrade your application over time instead of a full migration.
63
73
 
64
74
  To do that you need to configure the preset with `legacyV1Compatibility` option.
65
75
 
76
+ ## Options
77
+
66
78
  ```js
67
79
  // tailwind.config.js
68
80
 
69
81
  /** @type {import('tailwindcss').Config} */
70
82
  module.exports = {
71
83
  presets: [
72
- require('@obosbbl/grunnmuren-tailwind')({ legacyV1Compatibility: true }),
84
+ require('@obosbbl/grunnmuren-tailwind')({ includeFontFallback: false }),
73
85
  ],
86
+ // content: [ ... ]
74
87
  };
75
88
  ```
89
+
90
+ The preset supports the following options:
91
+
92
+ | Name | Default value | Description |
93
+ | --------------------- | ------------- | --------------------------------------------------- |
94
+ | includeFontFallback | `true` | Whether the preset includes a font fallback |
95
+ | legacyV1Compatibility | `false` | Configures partial compatibility with Grunnmuren v1 |
@@ -0,0 +1 @@
1
+ module.exports = {"OBOSText":{"font-family":"\"__OBOSText_Fallback\"","src":"local(\"Arial\")","size-adjust":"100%","ascent-override":"94%","descent-override":"26%","line-gap-override":"0%"},"OBOSDisplay":{"font-family":"\"__OBOSDisplay_Fallback\"","src":"local(\"Arial\")","size-adjust":"100%","ascent-override":"94%","descent-override":"26%","line-gap-override":"0%"}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obosbbl/grunnmuren-tailwind",
3
- "version": "2.0.0-canary.1",
3
+ "version": "2.0.0-canary.3",
4
4
  "description": "Grunnmuren Tailwind preset",
5
5
  "repository": {
6
6
  "url": "https://github.com/code-obos/grunnmuren"
@@ -11,16 +11,21 @@
11
11
  ".": "./tailwind-base.cjs"
12
12
  },
13
13
  "files": [
14
- "tailwind-base.cjs"
14
+ "tailwind-base.cjs",
15
+ "fonts/font-fallback.js"
15
16
  ],
16
17
  "dependencies": {
17
18
  "@tailwindcss/aspect-ratio": "^0.4.2",
18
- "@tailwindcss/typography": "^0.5.10"
19
+ "@tailwindcss/typography": "^0.5.10",
20
+ "tailwindcss-animate": "^1.0.7"
19
21
  },
20
22
  "devDependencies": {
21
- "tailwindcss": "3.3.5"
23
+ "tailwindcss": "3.4.3"
22
24
  },
23
25
  "peerDependencies": {
24
- "tailwindcss": "^3.3.5"
26
+ "tailwindcss": "^3.4.0"
27
+ },
28
+ "scripts": {
29
+ "font-fallback": "bun --cwd=./fonts ./generate-font-fallback.ts"
25
30
  }
26
31
  }
package/tailwind-base.cjs CHANGED
@@ -1,32 +1,147 @@
1
1
  const plugin = require('tailwindcss/plugin');
2
+ const fontFallbacks = require('./fonts/font-fallback');
2
3
 
3
- const obosFonts = [
4
- {
5
- fontWeight: 400,
6
- fontStyle: 'normal',
7
- url: 'https://www.obos.no/fonts/OBOSText-Regular.woff2',
4
+ const fontDeclarations = {
5
+ OBOSText: [
6
+ {
7
+ fontWeight: 400,
8
+ fontStyle: 'normal',
9
+ url: 'https://www.obos.no/fonts/OBOSText-Regular.woff2',
10
+ },
11
+ {
12
+ fontWeight: 400,
13
+ fontStyle: 'italic',
14
+ url: 'https://www.obos.no/fonts/OBOSText-Italic.woff2',
15
+ },
16
+ {
17
+ fontWeight: 500,
18
+ fontStyle: 'normal',
19
+ url: 'https://www.obos.no/fonts/OBOSText-Medium.woff2',
20
+ },
21
+ ],
22
+ OBOSDisplay: [
23
+ {
24
+ url: 'https://www.obos.no/fonts/OBOSDisplay-SemiBold.woff2',
25
+ },
26
+ ],
27
+ };
28
+
29
+ /**
30
+ * Styles for typography that are reused in both component classes and prose (through the tailwind typography plugin)
31
+ */
32
+ const typography = {
33
+ headingXlText: {
34
+ fontWeight: 'semibold',
35
+ small: {
36
+ fontSize: '2.8125rem',
37
+ lineHeight: '3.625rem',
38
+ },
39
+ large: {
40
+ fontSize: '3.9375rem',
41
+ lineHeight: '5.125rem',
42
+ },
8
43
  },
9
- {
10
- fontWeight: 400,
11
- fontStyle: 'italic',
12
- url: 'https://www.obos.no/fonts/OBOSText-Italic.woff2',
44
+ headingLText: {
45
+ fontWeight: 'semibold',
46
+ small: {
47
+ fontSize: '1.8125rem',
48
+ lineHeight: '2.75rem',
49
+ },
50
+ large: {
51
+ fontSize: '2.25rem',
52
+ lineHeight: '3.5rem',
53
+ },
13
54
  },
14
- {
15
- fontWeight: 500,
16
- fontStyle: 'normal',
17
- url: 'https://www.obos.no/fonts/OBOSText-Medium.woff2',
55
+ headingMText: {
56
+ fontWeight: 'medium',
57
+ small: {
58
+ fontSize: '1.4375rem',
59
+ lineHeight: '2.25rem',
60
+ },
61
+ large: {
62
+ fontSize: '1.625rem',
63
+ lineHeight: '2.5625rem',
64
+ },
18
65
  },
19
- {
20
- fontWeight: 700,
21
- fontStyle: 'normal',
22
- url: 'https://www.obos.no/fonts/OBOSText-Bold.woff2',
66
+ headingSText: {
67
+ fontWeight: 'medium',
68
+ small: {
69
+ fontSize: '1.1875rem',
70
+ lineHeight: '1.1875rem',
71
+ },
72
+ large: {
73
+ fontSize: '1.3125rem',
74
+ lineHeight: '2.125rem',
75
+ },
76
+ },
77
+ headingXsText: {
78
+ fontWeight: 'medium',
79
+ small: {
80
+ fontSize: '1.125rem',
81
+ lineHeight: '1.75rem',
82
+ },
83
+ large: {
84
+ fontSize: '1.1875rem',
85
+ lineHeight: '1.9375rem',
86
+ },
23
87
  },
24
- ];
88
+ paragraphText: {
89
+ fontSize: '1rem', // 1rem is the base font size, which is obviously the default size. But it is set explicitly here to make it easier to configure in the future, if this size changes.
90
+ lineHeight: '1.625rem',
91
+ },
92
+ leadText: {
93
+ fontWeight: 'medium',
94
+ small: {
95
+ fontSize: '1.4375rem',
96
+ lineHeight: '2.25rem',
97
+ },
98
+ large: {
99
+ fontSize: '1.625rem',
100
+ lineHeight: '2.5625rem',
101
+ },
102
+ },
103
+ blockquoteText: {
104
+ fontWeight: 'medium',
105
+ display: 'grid',
106
+ gridTemplateColumns: '3rem 1fr',
107
+ columnGap: '0.4375rem',
108
+ small: {
109
+ fontSize: '1.4375rem',
110
+ lineHeight: '2.25rem',
111
+ },
112
+ large: {
113
+ fontSize: '1.625rem',
114
+ lineHeight: '2.5625rem',
115
+ },
116
+ before: {
117
+ content: '"“"',
118
+ fontFamily: 'OBOSDisplay',
119
+ fontSize: '4.6875rem',
120
+ lineHeight: '1.6875rem',
121
+ fontWeight: '400',
122
+ fontStyle: 'normal',
123
+ },
124
+ },
125
+ descriptionText: {
126
+ small: {
127
+ fontSize: '0.875rem',
128
+ lineHeight: '1.4375rem',
129
+ },
130
+ large: {
131
+ fontSize: '0.875rem',
132
+ lineHeight: '1.375rem',
133
+ },
134
+ },
135
+ };
25
136
 
26
137
  /**
138
+ * @param {boolean} options.includeFontFallback
27
139
  * @param {boolean} options.legacyV1Compatibility
28
140
  */
29
141
  module.exports = (options = {}) => {
142
+ options.includeFontFallback ??= true;
143
+ options.legacyV1Compatibility ??= false;
144
+
30
145
  const v1CompatibilityPlugins = [];
31
146
 
32
147
  if (options.legacyV1Compatibility) {
@@ -39,18 +154,17 @@ module.exports = (options = {}) => {
39
154
  );
40
155
  }
41
156
 
42
- const fontFamily = 'OBOSFont';
43
- const fonts = obosFonts;
44
157
  const containerSize = '92rem';
45
158
 
46
159
  return {
47
160
  plugins: [
48
161
  ...v1CompatibilityPlugins,
49
162
  require('@tailwindcss/typography'),
50
- plugin(function ({ addBase, addComponents }) {
163
+ require('tailwindcss-animate'),
164
+ plugin(function ({ addBase, addComponents, theme }) {
51
165
  addBase({
52
166
  html: {
53
- '@apply text-black antialiased font-normal': {},
167
+ '@apply text-black antialiased font-normal font-text': {},
54
168
  },
55
169
  b: {
56
170
  fontWeight: 500,
@@ -65,6 +179,15 @@ module.exports = (options = {}) => {
65
179
  });
66
180
 
67
181
  addComponents({
182
+ '.page-layout': {
183
+ display: 'flex',
184
+ flexDirection: 'column',
185
+ minHeight: '100vh',
186
+ },
187
+ '.page-layout-main': {
188
+ backgroundColor: theme('colors.white'),
189
+ flexGrow: '1',
190
+ },
68
191
  '.container': {
69
192
  width: '100%',
70
193
  paddingLeft: '1rem',
@@ -85,56 +208,120 @@ module.exports = (options = {}) => {
85
208
  }),
86
209
 
87
210
  plugin(function ({ addBase, addComponents }) {
211
+ const {
212
+ headingXlText,
213
+ headingLText,
214
+ headingMText,
215
+ headingSText,
216
+ headingXsText,
217
+ paragraphText,
218
+ leadText,
219
+ blockquoteText,
220
+ descriptionText,
221
+ } = typography;
222
+
88
223
  // This is tailwind syntax for setting both the font-size and the line-height
89
- const h1 = '@apply font-bold text-[28px]/[38px] md:text-[40px]/[56px]';
90
- const h2 = '@apply font-bold text-[24px]/[30px] md:text-[32px]/[42px]';
91
- const h3 = '@apply font-bold text-[20px]/[30px] md:text-[24px]/[34px]';
92
- const h4 = '@apply font-bold text-[18px]/[24px] md:text-[20px]/[28px]';
224
+ const headingXl = `@apply font-display font-${headingXlText.fontWeight} text-[${headingXlText.small.fontSize}]/[${headingXlText.small.lineHeight}] md:text-[${headingXlText.large.fontSize}]/[${headingXlText.large.lineHeight}]`;
225
+ const headingL = `@apply font-display font-${headingLText.fontWeight} text-[${headingLText.small.fontSize}]/[${headingLText.small.lineHeight}] md:text-[${headingLText.large.fontSize}]/[${headingLText.large.lineHeight}]`;
226
+ const headingM = `@apply font-${headingMText.fontWeight} text-[${headingMText.small.fontSize}]/[${headingMText.small.lineHeight}] md:text-[${headingMText.large.fontSize}]/[${headingMText.large.lineHeight}]`;
227
+ const headingS = `@apply font-${headingSText.fontWeight} text-[${headingSText.small.fontSize}]/[${headingSText.small.lineHeight}] md:text-[${headingSText.large.fontSize}]/[${headingSText.large.lineHeight}]`;
228
+ const headingXs = `@apply font-${headingXsText.fontWeight} text-[${headingXsText.small.fontSize}]/[${headingXsText.small.lineHeight}] md:text-[${headingXsText.large.fontSize}]/[${headingXsText.large.lineHeight}]`;
229
+
230
+ const paragraph = `@apply text-[${paragraphText.fontSize}]/[${paragraphText.lineHeight}]`;
231
+ const lead = `@apply font-medium text-[${leadText.small.fontSize}]/[${leadText.small.lineHeight}] md:text-[${leadText.large.fontSize}]/[${leadText.large.lineHeight}]`;
232
+
233
+ const blockquote = `@apply font-${blockquoteText.fontWeight} italic grid grid-cols-[${blockquoteText.gridTemplateColumns.split(' ').join('_')}] gap-x-[${blockquoteText.columnGap}] pt-4
234
+ text-[${blockquoteText.large.fontSize}]/[${blockquoteText.large.lineHeight}] md:text-[${blockquoteText.small.fontSize}]/[${blockquoteText.small.lineHeight}]
235
+ before:text-[${blockquoteText.before.fontSize}]/[${blockquoteText.before.lineHeight}] before:content-[${blockquoteText.before.content}] before:font-display before:not-italic`;
236
+
237
+ const description = `@apply text-[${descriptionText.large.fontSize}]/[${descriptionText.large.lineHeight}] md:text-[${descriptionText.small.fontSize}]/[${descriptionText.small.lineHeight}]`;
93
238
 
94
239
  if (options.legacyV1Compatibility) {
95
240
  addBase({
96
241
  h1: {
97
- [h1]: {},
242
+ [headingXl]: {},
98
243
  },
99
244
  h2: {
100
- [h2]: {},
245
+ [headingL]: {},
101
246
  },
102
247
  h3: {
103
- [h3]: {},
248
+ [headingM]: {},
104
249
  },
105
250
  h4: {
106
- [h4]: {},
251
+ [headingS]: {},
107
252
  },
108
253
  });
109
254
  }
110
255
 
111
256
  addComponents({
257
+ /** @deprecated Will be replaced by heading-xl */
112
258
  '.h1': {
113
- [h1]: {},
259
+ [headingXl]: {},
114
260
  },
261
+ /** @deprecated Will be replaced by heading-l */
115
262
  '.h2': {
116
- [h2]: {},
263
+ [headingL]: {},
117
264
  },
265
+ /** @deprecated Will be replaced by heading-m */
118
266
  '.h3': {
119
- [h3]: {},
267
+ [headingM]: {},
120
268
  },
269
+ /** @deprecated Will be replaced by heading-s */
121
270
  '.h4': {
122
- [h4]: {},
271
+ [headingS]: {},
272
+ },
273
+ '.heading-xl': {
274
+ [headingXl]: {},
275
+ },
276
+ '.heading-l': {
277
+ [headingL]: {},
278
+ },
279
+ '.heading-m': {
280
+ [headingM]: {},
281
+ },
282
+ '.heading-s': {
283
+ [headingS]: {},
284
+ },
285
+ '.heading-xs': {
286
+ [headingXs]: {},
287
+ },
288
+ '.paragraph': {
289
+ [paragraph]: {},
290
+ },
291
+ '.lead': {
292
+ [lead]: {},
293
+ },
294
+ '.blockquote': {
295
+ [blockquote]: {},
296
+ },
297
+ '.description': {
298
+ [description]: {},
123
299
  },
124
300
  });
125
301
  }),
126
302
  plugin(function ({ addBase }) {
127
303
  addBase(
128
- fonts.map((font) => ({
129
- '@font-face': {
130
- fontFamily,
131
- fontWeight: font.fontWeight,
132
- fontStyle: font.fontStyle,
133
- src: `url('${font.url}') format('woff2')`,
134
- fontDisplay: 'swap',
135
- },
136
- })),
304
+ Object.entries(fontDeclarations).flatMap(
305
+ ([fontFamily, fontFamilyDeclarations]) =>
306
+ fontFamilyDeclarations.map((font) => ({
307
+ '@font-face': {
308
+ fontFamily,
309
+ fontWeight: font.fontWeight,
310
+ fontStyle: font.fontStyle,
311
+ src: `url('${font.url}') format('woff2')`,
312
+ fontDisplay: 'swap',
313
+ },
314
+ })),
315
+ ),
137
316
  );
317
+
318
+ if (options.includeFontFallback) {
319
+ addBase(
320
+ Object.values(fontFallbacks).map((fontFallback) => ({
321
+ '@font-face': fontFallback,
322
+ })),
323
+ );
324
+ }
138
325
  }),
139
326
  ],
140
327
  theme: {
@@ -191,7 +378,17 @@ module.exports = (options = {}) => {
191
378
  },
192
379
  },
193
380
  fontFamily: {
194
- sans: [fontFamily, 'sans-serif'],
381
+ text: [
382
+ 'OBOSText',
383
+ options.includeFontFallback && fontFallbacks.OBOSText['font-family'],
384
+ 'sans-serif',
385
+ ].filter((f) => f),
386
+ display: [
387
+ 'OBOSDisplay',
388
+ options.includeFontFallback &&
389
+ fontFallbacks.OBOSDisplay['font-family'],
390
+ 'sans-serif',
391
+ ],
195
392
  },
196
393
  extend: {
197
394
  maxWidth: {
@@ -215,8 +412,7 @@ module.exports = (options = {}) => {
215
412
  '--tw-prose-headings': 'inherit',
216
413
  '--tw-prose-lead': 'inherit',
217
414
  '--tw-prose-links': 'inherit',
218
- '--tw-prose-quotes': theme('colors.blue.dark'),
219
- '--tw-prose-quote-borders': theme('colors.green.DEFAULT'),
415
+ '--tw-prose-quotes': 'inherit',
220
416
  '--tw-prose-counters': theme('colors.black'),
221
417
  '--tw-prose-bullets': theme('colors.green.DEFAULT'),
222
418
  color: theme('colors.black'),
@@ -225,49 +421,84 @@ module.exports = (options = {}) => {
225
421
  fontWeight: 400,
226
422
  },
227
423
  h1: {
228
- fontWeight: theme('fontWeight.bold'),
229
- fontSize: theme('fontSize.3xl'),
424
+ fontFamily: 'OBOSDisplay',
425
+ fontWeight: theme('fontWeight.semibold'),
426
+ ...typography.headingXlText.small,
230
427
  '@media (min-width: theme("screens.md"))': {
231
- fontSize: theme('fontSize.5xl'),
428
+ ...typography.headingXlText.large,
232
429
  },
233
430
  },
234
431
  h2: {
235
- fontWeight: theme('fontWeight.bold'),
236
- fontSize: theme('fontSize.2xl'),
432
+ fontFamily: 'OBOSDisplay',
433
+ fontWeight: theme('fontWeight.semibold'),
434
+ ...typography.headingLText.small,
237
435
  '@media (min-width: theme("screens.md"))': {
238
- fontSize: theme('fontSize.4xl'),
436
+ ...typography.headingLText.large,
239
437
  },
240
438
  },
241
439
  h3: {
242
- fontWeight: theme('fontWeight.bold'),
243
- fontSize: theme('fontSize.xl'),
440
+ fontWeight: theme('fontWeight.medium'),
441
+ ...typography.headingMText.small,
244
442
  '@media (min-width: theme("screens.md"))': {
245
- fontSize: theme('fontSize.2xl'),
443
+ ...typography.headingMText.large,
246
444
  },
247
445
  },
248
446
  h4: {
447
+ fontWeight: theme('fontWeight.medium'),
448
+ ...typography.headingSText.small,
449
+ '@media (min-width: theme("screens.md"))': {
450
+ ...typography.headingSText.large,
451
+ },
452
+ },
453
+ h5: {
249
454
  fontWeight: theme('fontWeight.bold'),
250
- fontSize: theme('fontSize.lg'),
455
+ ...typography.headingXsText.small,
251
456
  '@media (min-width: theme("screens.md"))': {
252
- fontSize: theme('fontSize.xl'),
457
+ ...typography.headingXsText.large,
253
458
  },
254
459
  },
255
460
  li: {
256
461
  marginTop: '1.5em',
257
462
  marginBottom: '1.5em',
258
463
  },
259
- blockquote: {
260
- fontWeight: theme('fontWeight.bold'),
261
- fontStyle: 'normal',
464
+ p: {
465
+ ...typography.paragraphText.small,
466
+ '@media (min-width: theme("screens.md"))': {
467
+ ...typography.paragraphText.large,
468
+ },
262
469
  },
263
- 'blockquote p:first-of-type::before': {
264
- content: '"«"',
470
+ blockquote: {
471
+ // Reset defaults:
472
+ marginBottom: 'unset',
473
+ padding: 'unset',
474
+ border: 'unset',
475
+ fontWeight: theme('fontWeight.medium'),
476
+ fontStyle: 'italic',
477
+ display: typography.blockquoteText.display,
478
+ gridTemplateColumns:
479
+ typography.blockquoteText.gridTemplateColumns,
480
+ columnGap: typography.blockquoteText.columnGap,
481
+ paddingTop: '1rem',
482
+ ...typography.blockquoteText.small,
483
+ '@media (min-width: theme("screens.md"))': {
484
+ ...typography.blockquoteText.large,
485
+ },
265
486
  },
266
- 'blockquote p:last-of-type::after': {
267
- content: '"»"',
487
+ 'blockquote::before': {
488
+ ...typography.blockquoteText.before,
268
489
  },
269
490
  '[class~="lead"]': {
270
491
  fontWeight: theme('fontWeight.medium'),
492
+ ...typography.leadText.small,
493
+ '@media (min-width: theme("screens.md"))': {
494
+ ...typography.leadText.large,
495
+ },
496
+ },
497
+ '[class~="description"]': {
498
+ ...typography.descriptionText.small,
499
+ '@media (min-width: theme("screens.md"))': {
500
+ ...typography.descriptionText.large,
501
+ },
271
502
  },
272
503
  },
273
504
  },