@charcoal-ui/tailwind-config 4.0.0-beta.8 → 4.0.0-rc.1

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.
@@ -49,7 +49,6 @@ export class TailwindBuild {
49
49
  const plugin = tailwindcss({
50
50
  ...config,
51
51
 
52
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
53
52
  // @ts-expect-error safelist が @types/tailwindcss に生えてない
54
53
  safelist: [
55
54
  {
@@ -90,9 +89,9 @@ export class TailwindBuild {
90
89
  const cssVariables = new Set<string>()
91
90
 
92
91
  /**
93
- * 独自に生成する CSS 変数は必ず --tailwind で始まるはず
92
+ * 独自に生成する CSS 変数は必ず --(tailwind|charcoal) で始まるはず
94
93
  */
95
- this.result.root.walkDecls(/^--tailwind/u, (decl) => {
94
+ this.result.root.walkDecls(/^--(tailwind|charcoal)/u, (decl) => {
96
95
  cssVariables.add(decl.prop)
97
96
  })
98
97
 
@@ -102,7 +101,7 @@ export class TailwindBuild {
102
101
  getCssVariable(varName: `--${string}`) {
103
102
  const values: string[] = []
104
103
 
105
- this.result.root.walkDecls(/^--tailwind/u, (decl) => {
104
+ this.result.root.walkDecls(/^--(tailwind|charcoal)/u, (decl) => {
106
105
  if (decl.prop === varName) {
107
106
  values.push(decl.value)
108
107
  }
@@ -10,19 +10,30 @@ import plugin, { TailwindPlugin } from 'tailwindcss/plugin'
10
10
  import { mergeEffect } from '../foundation'
11
11
  import { CSSVariableName, CSSVariables, Definition, ThemeMap } from '../types'
12
12
  import { COLOR_PREFIX, isSingleColor } from './utils'
13
+ import { defineCssVariablesV1 } from './pluginTokenV1'
13
14
 
14
15
  /**
15
- * `:root` 以外のケースで各 CSS Variable がどういう値を取るかを定義する
16
+ * --tailwind-* また --charcoal-* を生成する
17
+ * TODO: --tailwindをやめる
16
18
  */
17
- export default function cssVariableColorPlugin({
18
- ':root': _defaultTheme,
19
- ...themes
20
- }: ThemeMap): TailwindPlugin {
21
- const definitions = defineCssVariables(themes)
19
+ export default function cssVariableColorPlugin(
20
+ themeMap: ThemeMap,
21
+ cssVariablesV1: boolean
22
+ ): TailwindPlugin {
23
+ // `:root` 以外のケースで各 CSS Variable がどういう値を取るかを定義する
24
+ const { ':root': _defaultTheme, ...otherThemes } = themeMap
25
+ const definitions = defineCssVariables(otherThemes)
22
26
 
23
27
  return plugin(({ addBase }) => {
24
28
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
25
29
  addBase(definitions)
30
+
31
+ // styledのTokenInjector移植(background処理除く)
32
+ if (cssVariablesV1) {
33
+ const cssVariablesV1 = defineCssVariablesV1(themeMap)
34
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
35
+ addBase(cssVariablesV1)
36
+ }
26
37
  })
27
38
  }
28
39
 
@@ -0,0 +1,99 @@
1
+ import {
2
+ applyEffect,
3
+ customPropertyToken,
4
+ filterObject,
5
+ flatMapObject,
6
+ mapObject,
7
+ } from '@charcoal-ui/utils'
8
+ import { ThemeMap } from '../types'
9
+ import {
10
+ CharcoalAbstractTheme,
11
+ EffectType,
12
+ Key,
13
+ CharcoalTheme as Theme,
14
+ } from '@charcoal-ui/theme'
15
+
16
+ export function defineCssVariablesV1(themeMap: ThemeMap) {
17
+ // @ts-expect-error FIXME
18
+ return mapObject(themeMap, (key, theme) => {
19
+ if (key.startsWith('@media')) {
20
+ return [
21
+ key,
22
+ {
23
+ ':root': defineColorVariableCSS(theme),
24
+ },
25
+ ]
26
+ } else {
27
+ return [key, defineColorVariableCSS(theme)]
28
+ }
29
+ })
30
+ }
31
+
32
+ export const defineColorVariableCSS = (theme: Theme) => {
33
+ const borders = mapObject(theme.border, (name, { color }) => [
34
+ // REVIEW: もしtheme.colorにたまたまborder-〇〇で始まる色名がいたら被りうる
35
+ withPrefixes('border', name),
36
+ color,
37
+ ])
38
+
39
+ const colors = defineThemeVariables({ ...theme.color, ...borders })({ theme })
40
+ return colors
41
+ }
42
+
43
+ /**
44
+ * Check whether a value is non-null and non-undefined
45
+ *
46
+ * @param value nullable
47
+ */
48
+ export const isPresent = <T>(value: T): value is NonNullable<T> => value != null
49
+
50
+ /**
51
+ * 子孫要素で使われるカラーテーマの CSS Variables を上書きする
52
+ *
53
+ * @params colorParams - 上書きしたい色の定義( `theme.color` の一部だけ書けば良い )
54
+ * @params effectParams - effect の定義を上書きしたい場合は渡す(必須ではない)
55
+ *
56
+ * @example
57
+ * ```tsx
58
+ * const LocalTheme = styled.div`
59
+ * ${defineThemeVariables({ text1: '#ff0000' })}
60
+ * // `text1` is now defined as red
61
+ * ${theme((o) => [o.font.text1])}
62
+ * `
63
+ * ```
64
+ */
65
+ export function defineThemeVariables(
66
+ colorParams: Partial<CharcoalAbstractTheme['color']>,
67
+ effectParams?: Partial<CharcoalAbstractTheme['effect']>
68
+ ) {
69
+ return function toCssObject(props: {
70
+ theme: Pick<CharcoalAbstractTheme, 'effect'>
71
+ }) {
72
+ const colors = filterObject(colorParams, isPresent)
73
+
74
+ // flatMapObject の中で毎回 Object.entries を呼ぶのは無駄なので外で呼ぶ
75
+ const effects = Object.entries({
76
+ ...props.theme.effect,
77
+ ...effectParams,
78
+ })
79
+
80
+ return flatMapObject(colors, (colorKey, color) => [
81
+ [customPropertyToken(colorKey), color],
82
+
83
+ ...effects.map<[string, string]>(([effectKey, effect]) => [
84
+ customPropertyToken(colorKey, [effectKey]),
85
+ applyEffect(color, [effect]),
86
+ ]),
87
+ ])
88
+ }
89
+ }
90
+
91
+ export function isSupportedEffect(effect: Key): effect is EffectType {
92
+ return ['hover', 'press', 'disabled'].includes(effect as string)
93
+ }
94
+
95
+ export const variable = (value: string) => `var(${value})`
96
+
97
+ export function withPrefixes(...parts: string[]) {
98
+ return parts.join('-')
99
+ }
@@ -1,6 +1,6 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`Storybook Tests tailwind-config/Colors/Text bg color Playground 1`] = `
3
+ exports[`Storybook Tests > tailwind-config/Colors/Text bg color > Playground 1`] = `
4
4
  <div
5
5
  data-dark={false}
6
6
  >
package/src/index.test.ts CHANGED
@@ -11,6 +11,10 @@ describe('tailwind.config.js', () => {
11
11
  },
12
12
  })
13
13
 
14
+ test('defaultConfig', () => {
15
+ expect(defaultConfig).toMatchSnapshot()
16
+ })
17
+
14
18
  let result: TailwindBuild
15
19
 
16
20
  beforeAll(async () => {
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { GRID_COUNT, mergeEffect } from './foundation'
2
2
 
3
- import type { TailwindConfig } from 'tailwindcss/tailwind-config'
3
+ import type { TailwindConfig, TailwindTheme } from 'tailwindcss/tailwind-config'
4
4
  import { TailwindVersion, ThemeMap } from './types'
5
5
 
6
6
  import {
@@ -21,15 +21,21 @@ import { colorsToTailwindConfig } from './colors/toTailwindConfig'
21
21
  import cssVariableColorPlugin from './colors/plugin'
22
22
  import cssVariableGradientPlugin from './gradient/plugin'
23
23
  import typographyPlugin from './typography/plugin'
24
+ import { unstable_createTailwindConfigTokenV2 } from './tokenV2'
25
+ export { unstable_createTailwindConfigTokenV2 }
24
26
 
25
27
  interface Options {
26
28
  version?: TailwindVersion
27
29
  theme?: ThemeMap
30
+ cssVariablesV1?: boolean
31
+ unstableTokenV2?: boolean
28
32
  }
29
33
 
30
34
  export function createTailwindConfig({
31
35
  theme = { ':root': light },
32
36
  version = 'v3',
37
+ cssVariablesV1 = true,
38
+ unstableTokenV2 = false,
33
39
  }: Options): TailwindConfig {
34
40
  assertAllThemeHaveSameKeys(theme)
35
41
 
@@ -37,6 +43,20 @@ export function createTailwindConfig({
37
43
  const effects = mergeEffect(defaultTheme)
38
44
  const DEFAULT = getDefaultKeyName(version)
39
45
 
46
+ const {
47
+ borderWidth: borderWidthV2,
48
+ borderRadius: borderRadiusV2,
49
+ borderColor: borderColorV2,
50
+ colors: colorsV2,
51
+ fontSize: fontSizeV2,
52
+ fontWeight: fontWeightV2,
53
+ spacing: spacingV2,
54
+ gap: gapV2,
55
+ width: widthV2,
56
+ }: Partial<TailwindTheme> = unstableTokenV2
57
+ ? unstable_createTailwindConfigTokenV2().theme
58
+ : {}
59
+
40
60
  return {
41
61
  theme: {
42
62
  screens: {
@@ -56,6 +76,7 @@ export function createTailwindConfig({
56
76
  transparent: 'transparent',
57
77
  current: 'currentColor',
58
78
  ...colorsToTailwindConfig(version, defaultTheme.color, effects),
79
+ ...colorsV2,
59
80
  },
60
81
  borderColor: {
61
82
  ...colorsToTailwindConfig(
@@ -63,8 +84,15 @@ export function createTailwindConfig({
63
84
  mapObject(defaultTheme.border, (k, v) => [k, v.color]),
64
85
  effects
65
86
  ),
87
+ ...borderColorV2,
88
+ },
89
+ spacing: {
90
+ ...mapObject(
91
+ SPACING,
92
+ (name, pixel) => [name, px(pixel)] as [string, string]
93
+ ),
94
+ ...spacingV2,
66
95
  },
67
- spacing: mapObject(SPACING, (name, pixel) => [name, px(pixel)]),
68
96
  width: {
69
97
  full: '100%',
70
98
  screen: '100vw',
@@ -92,29 +120,40 @@ export function createTailwindConfig({
92
120
  }),
93
121
  {}
94
122
  ),
123
+ ...widthV2,
95
124
  },
96
125
  gap: {
97
126
  fixed: px(GUTTER_UNIT),
127
+ ...gapV2,
128
+ },
129
+ borderRadius: {
130
+ ...mapObject(
131
+ BORDER_RADIUS,
132
+ (name, value) => [name, px(value)] as [string, string]
133
+ ),
134
+ ...borderRadiusV2,
98
135
  },
99
- borderRadius: mapObject(BORDER_RADIUS, (name, value) => [
100
- name,
101
- px(value),
102
- ]),
103
136
  transitionDuration: {
104
137
  [DEFAULT]: '0.2s',
105
138
  },
139
+ ...(unstableTokenV2
140
+ ? {
141
+ borderWidth: borderWidthV2,
142
+ fontSize: fontSizeV2,
143
+ fontWeight: fontWeightV2,
144
+ }
145
+ : {}),
106
146
  },
107
147
 
108
148
  ...getVariantOption(version),
109
149
 
110
150
  corePlugins: {
111
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
112
151
  // @ts-expect-error 配列にしろと言ってくるが、たぶん @types が間違っている
113
152
  lineHeight: false,
114
153
  },
115
154
  plugins: [
116
155
  typographyPlugin,
117
- cssVariableColorPlugin(theme),
156
+ cssVariableColorPlugin(theme, Boolean(cssVariablesV1)),
118
157
 
119
158
  ...Object.entries(theme).map(([selectorOrMediaQuery, theme]) =>
120
159
  cssVariableGradientPlugin(
@@ -0,0 +1,21 @@
1
+ import { TailwindBuild } from './_lib/TailwindBuild'
2
+ import { unstable_createTailwindConfigTokenV2 } from './tokenV2'
3
+
4
+ describe('unstable_createTailwindConfigTokenV2', async () => {
5
+ const config = unstable_createTailwindConfigTokenV2()
6
+ const result = await TailwindBuild.run(
7
+ config,
8
+ `
9
+ @import 'tailwindcss/base';
10
+ @import 'tailwindcss/utilities';
11
+ @import 'tailwindcss/components';
12
+ `
13
+ )
14
+ test('config object', () => {
15
+ expect(config).toMatchSnapshot()
16
+ })
17
+
18
+ test('list of classes', () => {
19
+ expect(result.classNames).toMatchSnapshot()
20
+ })
21
+ })
package/src/tokenV2.ts ADDED
@@ -0,0 +1,72 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
3
+ import light from '@charcoal-ui/theme/unstable-tokens/css-variables.json'
4
+ import {
5
+ TailwindConfig,
6
+ TailwindThemeFontSizes,
7
+ } from 'tailwindcss/tailwind-config'
8
+ import {
9
+ flattenKey as flattenKeys,
10
+ mapDefaultKey as mapDefaultKeys,
11
+ } from './util'
12
+
13
+ export function unstable_createTailwindConfigTokenV2() {
14
+ const fontSize = Object.fromEntries(
15
+ Object.entries(light.text['font-size']).flatMap(([k, v]) => {
16
+ // text.fontSize.paragraph + text.lineHeight.paragraph -> text-paragraph
17
+ if (typeof v === 'string') {
18
+ return [
19
+ [
20
+ k,
21
+ [
22
+ v,
23
+ // @ts-expect-error k is keyof line-height
24
+ { lineHeight: light.text['line-height'][k] },
25
+ ],
26
+ ],
27
+ ]
28
+ }
29
+
30
+ // text.fontSize.heading.s + text.lineHeight.heading.s -> text-heading-s
31
+ return Object.entries(v as Record<string, string>).map(([kk, vv]) => {
32
+ return [
33
+ [k, kk].join('-'),
34
+ [
35
+ vv,
36
+ // @ts-expect-error k is keyof line-height
37
+ { lineHeight: light.text['line-height'][k][kk] },
38
+ ],
39
+ ]
40
+ })
41
+ })
42
+ ) as TailwindThemeFontSizes
43
+
44
+ // space.target.s -> p-target-s
45
+ // space.gap.gapButtons -> p-gap-buttons
46
+ const spacing = flattenKeys(light.space, (key) => !/(gap|padding)/.test(key))
47
+ // color.container.default -> bg-container
48
+ // color.container.hover -> bg-container-hover
49
+ const colors = mapDefaultKeys(light.color)
50
+
51
+ const config: TailwindConfig = {
52
+ darkMode: 'media',
53
+ theme: {
54
+ // borderWidth.m -> border-m
55
+ // borderWidth.focus.1 -> border-focus-1
56
+ borderWidth: flattenKeys(light['border-width']),
57
+ borderRadius: light.radius,
58
+ borderColor: flattenKeys(colors.border),
59
+
60
+ colors,
61
+
62
+ fontSize,
63
+ fontWeight: light.text['font-weight'],
64
+
65
+ spacing: spacing,
66
+ gap: spacing,
67
+ width: light['paragraph-width'],
68
+ },
69
+ }
70
+
71
+ return config
72
+ }
package/src/util.ts CHANGED
@@ -71,3 +71,32 @@ export function camelToKebab(value: string) {
71
71
  .replace(/(?<small>[\da-z]|(?=[A-Z]))(?<capital>[A-Z])/gu, '$1-$2')
72
72
  .toLowerCase()
73
73
  }
74
+
75
+ export const mapDefaultKey = <O extends object>(o: O) => {
76
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
77
+ return JSON.parse(JSON.stringify(o), function reviver(k: string, v: string) {
78
+ if (k === 'default') {
79
+ const DefaultKey = getDefaultKeyName('v3')
80
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
81
+ this[DefaultKey] = v
82
+ return undefined
83
+ }
84
+ return v
85
+ })
86
+ }
87
+
88
+ export const flattenKey = <O extends object>(
89
+ o: O,
90
+ join?: (key: string) => boolean
91
+ ) => {
92
+ return Object.fromEntries(
93
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
94
+ // @ts-ignore
95
+ Object.entries(o).flatMap(([key, v]) => {
96
+ if (typeof v === 'string') return [[key, v]]
97
+ return Object.entries(v as object).map(([kk, vv]) => {
98
+ return [join?.(key) ?? true ? [key, kk].join('-') : kk, vv]
99
+ })
100
+ })
101
+ )
102
+ }