@graphprotocol/gds-css 0.0.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.
Files changed (136) hide show
  1. package/README.md +80 -0
  2. package/dist/component-registry.d.ts +90 -0
  3. package/dist/component-registry.d.ts.map +1 -0
  4. package/dist/component-registry.js +93 -0
  5. package/dist/component-registry.js.map +1 -0
  6. package/dist/css-props/getCSSPropRawValue.d.ts +4 -0
  7. package/dist/css-props/getCSSPropRawValue.d.ts.map +1 -0
  8. package/dist/css-props/getCSSPropRawValue.js +41 -0
  9. package/dist/css-props/getCSSPropRawValue.js.map +1 -0
  10. package/dist/css-props/index.d.ts +5 -0
  11. package/dist/css-props/index.d.ts.map +1 -0
  12. package/dist/css-props/index.js +5 -0
  13. package/dist/css-props/index.js.map +1 -0
  14. package/dist/css-props/parseCSSPropValue.d.ts +7 -0
  15. package/dist/css-props/parseCSSPropValue.d.ts.map +1 -0
  16. package/dist/css-props/parseCSSPropValue.js +70 -0
  17. package/dist/css-props/parseCSSPropValue.js.map +1 -0
  18. package/dist/css-props/registerCSSProps.d.ts +7 -0
  19. package/dist/css-props/registerCSSProps.d.ts.map +1 -0
  20. package/dist/css-props/registerCSSProps.js +231 -0
  21. package/dist/css-props/registerCSSProps.js.map +1 -0
  22. package/dist/css-props/types.d.ts +29 -0
  23. package/dist/css-props/types.d.ts.map +1 -0
  24. package/dist/css-props/types.js +2 -0
  25. package/dist/css-props/types.js.map +1 -0
  26. package/dist/css-states/index.d.ts +3 -0
  27. package/dist/css-states/index.d.ts.map +1 -0
  28. package/dist/css-states/index.js +3 -0
  29. package/dist/css-states/index.js.map +1 -0
  30. package/dist/css-states/registerCSSStates.d.ts +23 -0
  31. package/dist/css-states/registerCSSStates.d.ts.map +1 -0
  32. package/dist/css-states/registerCSSStates.js +119 -0
  33. package/dist/css-states/registerCSSStates.js.map +1 -0
  34. package/dist/css-states/states.d.ts +71 -0
  35. package/dist/css-states/states.d.ts.map +1 -0
  36. package/dist/css-states/states.js +140 -0
  37. package/dist/css-states/states.js.map +1 -0
  38. package/dist/design-tokens.generated.d.ts +1167 -0
  39. package/dist/design-tokens.generated.d.ts.map +1 -0
  40. package/dist/design-tokens.generated.js +2675 -0
  41. package/dist/design-tokens.generated.js.map +1 -0
  42. package/dist/index.d.ts +7 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +7 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/tailwind-customizations/index.d.ts +3 -0
  47. package/dist/tailwind-customizations/index.d.ts.map +1 -0
  48. package/dist/tailwind-customizations/index.js +3 -0
  49. package/dist/tailwind-customizations/index.js.map +1 -0
  50. package/dist/tailwind-customizations/registerUtilities.d.ts +9 -0
  51. package/dist/tailwind-customizations/registerUtilities.d.ts.map +1 -0
  52. package/dist/tailwind-customizations/registerUtilities.js +59 -0
  53. package/dist/tailwind-customizations/registerUtilities.js.map +1 -0
  54. package/dist/tailwind-customizations/registerVariants.d.ts +8 -0
  55. package/dist/tailwind-customizations/registerVariants.d.ts.map +1 -0
  56. package/dist/tailwind-customizations/registerVariants.js +197 -0
  57. package/dist/tailwind-customizations/registerVariants.js.map +1 -0
  58. package/dist/tailwind-customizations/variants.d.ts +72 -0
  59. package/dist/tailwind-customizations/variants.d.ts.map +1 -0
  60. package/dist/tailwind-customizations/variants.js +153 -0
  61. package/dist/tailwind-customizations/variants.js.map +1 -0
  62. package/dist/tailwind-plugin.d.ts +4 -0
  63. package/dist/tailwind-plugin.d.ts.map +1 -0
  64. package/dist/tailwind-plugin.js +12 -0
  65. package/dist/tailwind-plugin.js.map +1 -0
  66. package/dist/types.d.ts +4 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/types.js +2 -0
  69. package/dist/types.js.map +1 -0
  70. package/dist/utils/cssUnescape.d.ts +20 -0
  71. package/dist/utils/cssUnescape.d.ts.map +1 -0
  72. package/dist/utils/cssUnescape.js +44 -0
  73. package/dist/utils/cssUnescape.js.map +1 -0
  74. package/dist/utils/index.d.ts +6 -0
  75. package/dist/utils/index.d.ts.map +1 -0
  76. package/dist/utils/index.js +6 -0
  77. package/dist/utils/index.js.map +1 -0
  78. package/dist/utils/pxToTw.d.ts +3 -0
  79. package/dist/utils/pxToTw.d.ts.map +1 -0
  80. package/dist/utils/pxToTw.js +5 -0
  81. package/dist/utils/pxToTw.js.map +1 -0
  82. package/dist/utils/twToPx.d.ts +3 -0
  83. package/dist/utils/twToPx.d.ts.map +1 -0
  84. package/dist/utils/twToPx.js +5 -0
  85. package/dist/utils/twToPx.js.map +1 -0
  86. package/dist/utils/twToRem.d.ts +3 -0
  87. package/dist/utils/twToRem.d.ts.map +1 -0
  88. package/dist/utils/twToRem.js +5 -0
  89. package/dist/utils/twToRem.js.map +1 -0
  90. package/dist/utils/wrapSelector.d.ts +10 -0
  91. package/dist/utils/wrapSelector.d.ts.map +1 -0
  92. package/dist/utils/wrapSelector.js +57 -0
  93. package/dist/utils/wrapSelector.js.map +1 -0
  94. package/package.json +65 -0
  95. package/src/component-registry.ts +213 -0
  96. package/src/css-props/getCSSPropRawValue.ts +52 -0
  97. package/src/css-props/index.ts +4 -0
  98. package/src/css-props/parseCSSPropValue.ts +81 -0
  99. package/src/css-props/registerCSSProps.ts +274 -0
  100. package/src/css-props/types.ts +35 -0
  101. package/src/css-states/index.ts +2 -0
  102. package/src/css-states/registerCSSStates.ts +136 -0
  103. package/src/css-states/states.ts +160 -0
  104. package/src/design-tokens.generated.ts +2799 -0
  105. package/src/index.ts +6 -0
  106. package/src/tailwind-customizations/index.ts +2 -0
  107. package/src/tailwind-customizations/registerUtilities.ts +65 -0
  108. package/src/tailwind-customizations/registerVariants.ts +296 -0
  109. package/src/tailwind-customizations/variants.ts +190 -0
  110. package/src/tailwind-plugin.ts +14 -0
  111. package/src/types.ts +4 -0
  112. package/src/utils/cssUnescape.ts +49 -0
  113. package/src/utils/index.ts +5 -0
  114. package/src/utils/pxToTw.ts +4 -0
  115. package/src/utils/twToPx.ts +4 -0
  116. package/src/utils/twToRem.ts +4 -0
  117. package/src/utils/wrapSelector.ts +60 -0
  118. package/styles/fonts/EuclidCircularA-Bold.woff2 +0 -0
  119. package/styles/fonts/EuclidCircularA-BoldItalic.woff2 +0 -0
  120. package/styles/fonts/EuclidCircularA-Light.woff2 +0 -0
  121. package/styles/fonts/EuclidCircularA-LightItalic.woff2 +0 -0
  122. package/styles/fonts/EuclidCircularA-Medium.woff2 +0 -0
  123. package/styles/fonts/EuclidCircularA-MediumItalic.woff2 +0 -0
  124. package/styles/fonts/EuclidCircularA-Regular.woff2 +0 -0
  125. package/styles/fonts/EuclidCircularA-RegularItalic.woff2 +0 -0
  126. package/styles/fonts/EuclidCircularA-Semibold.woff2 +0 -0
  127. package/styles/fonts/EuclidCircularA-SemiboldItalic.woff2 +0 -0
  128. package/styles/fonts.css +83 -0
  129. package/styles/global.css +203 -0
  130. package/styles/layers.css +8 -0
  131. package/styles/tailwind.css +13 -0
  132. package/styles/tailwind.vscode.css +11 -0
  133. package/styles/theme.css +420 -0
  134. package/styles/typography.css +198 -0
  135. package/styles/utilities.css +305 -0
  136. package/styles/variants.css +34 -0
@@ -0,0 +1,136 @@
1
+ import cssesc from 'cssesc'
2
+ import { objectFromEntries } from 'ts-extras'
3
+
4
+ import type { PluginAPI } from '../types.ts'
5
+ import { cssStates, cssStateVariables, getCSSStateVariable } from './states.ts'
6
+
7
+ /**
8
+ * Registers CSS States functionality in the Tailwind plugin.
9
+ *
10
+ * This system abstracts away CSS's native states in order to:
11
+ *
12
+ * - Allow overriding the state of components (e.g. `<Button className="state-active" />`).
13
+ * - Allow reading the state of components from their root element even when the native state is
14
+ * actually on a nested element (e.g. `group-has-checkbox-checked`), thanks to
15
+ * `variants/plugin.ts` overriding some of Tailwind's built-in variants.
16
+ * - Allow setting and reading custom states for DRY purposes (e.g. `active:state-[highlighted]` and
17
+ * `data-[highlighted]:state-[highlighted]` on a parent element and
18
+ * `@state-[highlighted]:bg-strong` on a child; also supports key-values like `state-[foo=bar]
19
+ * lg:state-[foo=baz]`).
20
+ *
21
+ * While this system is separate from the CSS Props one, it also leverages style queries and relies
22
+ * on the fact that component classes (e.g. `gds-button`) establish a container that can be queried.
23
+ * Furthermore, it uses some of the same techniques in the fallback implementation (querying by data
24
+ * attribute, `view-transition-name` to target browsers that don't support style queries, etc.),
25
+ * though the CSS props polyfill is not used. See `useCSSState` for more details.
26
+ */
27
+ export function registerCSSStates(api: PluginAPI) {
28
+ /**
29
+ * Register a couple custom properties for each state variable.
30
+ *
31
+ * - All of them are non-inherited to prevent leaking.
32
+ * - Normally, we would only need `--gds-exposed-*` and `--gds-state-*` (with no `initial-value` on
33
+ * `--gds-exposed-*`), but due to a limitation in the `style-observer` library
34
+ * (https://github.com/LeaVerou/style-observer/issues/133), we need to set an `initial-value` on
35
+ * any custom property we want to observe in JS. But we also need `--gds-state-*` to NOT have an
36
+ * initial value so that we can define a fallback when a state is not set at all (see
37
+ * `--gds-clickable-*`). So we register an additional `--gds-observed-*` property that is
38
+ * guaranteed to have a valid value and can be observed.
39
+ */
40
+ for (const [variableName, { values, defaultValue }] of Object.entries(cssStateVariables)) {
41
+ api.addBase({
42
+ [`@property --gds-exposed-${variableName}`]: {
43
+ syntax: "'*'",
44
+ inherits: 'false',
45
+ 'initial-value': '',
46
+ },
47
+ [`@property --gds-state-${variableName}`]: {
48
+ syntax: "'*'",
49
+ inherits: 'false',
50
+ },
51
+ [`@property --gds-observed-${variableName}`]: {
52
+ syntax: `'${[...new Set<typeof defaultValue>([...Object.values(values), defaultValue])].map((value) => String(value)).join(' | ')}'`,
53
+ inherits: 'false',
54
+ 'initial-value': String(defaultValue),
55
+ },
56
+ })
57
+ }
58
+
59
+ /**
60
+ * Register `expose-{state}` utilities for components to use internally to expose their current
61
+ * state if the root element doesn't already do so (e.g. by being a button with the expected
62
+ * attributes on it), as well as `state-{state}` utilities for consumers to trigger the
63
+ * hover/focus/etc. state of components that support it.
64
+ */
65
+ api.matchUtilities(
66
+ {
67
+ expose: (state) => {
68
+ const stateVariable = getCSSStateVariable(state)
69
+ return {
70
+ [`--gds-exposed-${stateVariable.name}`]: stateVariable.value,
71
+ [`--gds-state-${stateVariable.name}`]: stateVariable.value,
72
+ [`--gds-observed-${stateVariable.name}`]: stateVariable.value,
73
+ }
74
+ },
75
+ state: (state) => {
76
+ const stateVariable = getCSSStateVariable(state)
77
+ return {
78
+ [`--gds-state-${stateVariable.name}`]: stateVariable.value,
79
+ [`--gds-observed-${stateVariable.name}`]: stateVariable.value,
80
+ }
81
+ },
82
+ },
83
+ { values: Object.fromEntries(cssStates.map((state) => [state, state])) },
84
+ )
85
+
86
+ /** Register `@state-*` and `@state-not-*` variants for components to use internally. */
87
+ for (const variant of ['@state-not', '@state'] as const) {
88
+ api.matchVariant(
89
+ variant,
90
+ (state, { modifier }) => {
91
+ const containerName = modifier !== null ? `gds-${modifier}` : ''
92
+ const stateVariables = [getCSSStateVariable(state)]
93
+ // Make sure that `@state-hover` utilities are also applied on the active state
94
+ if (state === 'hover') {
95
+ stateVariables.push(getCSSStateVariable('active'))
96
+ }
97
+ const selectors = [
98
+ `@container ${containerName} ${variant === '@state-not' ? 'not' : ''} (${stateVariables
99
+ .flatMap((stateVariable) => {
100
+ const conditions = [
101
+ `style(--gds-state-${stateVariable.name}${stateVariable.value ? `: ${stateVariable.value}` : ''})`,
102
+ ]
103
+ if (stateVariable.isDefault) {
104
+ conditions.push(`(not style(--gds-state-${stateVariable.name}))`)
105
+ }
106
+ return conditions
107
+ })
108
+ .join(' or ')})`,
109
+ ]
110
+ if (containerName) {
111
+ // Fallback selector for browsers without style query support; see `css-props/registerCSSProps.ts` for more details
112
+ selectors.push(
113
+ `@supports (not (view-transition-name: none)) or (-moz-orient: inline) { &:where(.${containerName}${variant === '@state-not' ? ':not' : ':is'}(${stateVariables
114
+ .map(
115
+ (stateVariable) =>
116
+ `[data-gds-state-polyfill-${stateVariable.name}${stateVariable.value ? `=${cssesc(stateVariable.value, { isIdentifier: true })}` : ''}]`,
117
+ )
118
+ .join(', ')}) *) }`,
119
+ )
120
+ }
121
+ return selectors
122
+ },
123
+ {
124
+ values: objectFromEntries(
125
+ cssStates
126
+ .filter((state) => {
127
+ // Prevent creating `@state-not-not-*` variants
128
+ if (variant === '@state-not' && state.startsWith('not-')) return false
129
+ return true
130
+ })
131
+ .map((state) => [state, state]),
132
+ ),
133
+ },
134
+ )
135
+ }
136
+ }
@@ -0,0 +1,160 @@
1
+ import { wrapSelector } from '../utils/index.ts'
2
+
3
+ export const cssStates = [
4
+ 'open',
5
+ 'not-open',
6
+ 'current',
7
+ 'not-current',
8
+ 'checked',
9
+ 'indeterminate',
10
+ 'unchecked',
11
+ 'read-only',
12
+ 'blank',
13
+ 'not-blank',
14
+ 'idle',
15
+ 'hover',
16
+ 'focus',
17
+ 'not-focus',
18
+ 'active',
19
+ 'enabled',
20
+ 'disabled',
21
+ ] as const
22
+
23
+ export type CSSState = (typeof cssStates)[number]
24
+ export type CSSStateValue = string | boolean
25
+
26
+ function createCSSStateVariable<
27
+ K extends CSSState,
28
+ V extends CSSStateValue,
29
+ T extends Partial<Record<K, V>>,
30
+ >(variable: { values: T & Record<K, V>; defaultValue: T[keyof T] }) {
31
+ return variable
32
+ }
33
+
34
+ export const cssStateVariables = {
35
+ pointer: createCSSStateVariable({
36
+ values: {
37
+ idle: 'idle',
38
+ hover: 'hover',
39
+ active: 'active',
40
+ },
41
+ defaultValue: 'idle',
42
+ }),
43
+ focus: createCSSStateVariable({
44
+ values: {
45
+ focus: true,
46
+ 'not-focus': false,
47
+ },
48
+ defaultValue: false,
49
+ }),
50
+ checked: createCSSStateVariable({
51
+ values: {
52
+ checked: true,
53
+ indeterminate: 'indeterminate',
54
+ unchecked: false,
55
+ },
56
+ defaultValue: false,
57
+ }),
58
+ disabled: createCSSStateVariable({
59
+ values: {
60
+ enabled: false,
61
+ disabled: true,
62
+ 'read-only': 'read-only',
63
+ },
64
+ defaultValue: false,
65
+ }),
66
+ open: createCSSStateVariable({
67
+ values: {
68
+ open: true,
69
+ 'not-open': false,
70
+ },
71
+ defaultValue: false,
72
+ }),
73
+ current: createCSSStateVariable({
74
+ values: {
75
+ current: true,
76
+ 'not-current': false,
77
+ },
78
+ defaultValue: false,
79
+ }),
80
+ blank: createCSSStateVariable({
81
+ values: {
82
+ blank: true,
83
+ 'not-blank': false,
84
+ },
85
+ defaultValue: false,
86
+ }),
87
+ }
88
+
89
+ function isCSSState(state: unknown): state is CSSState {
90
+ return cssStates.includes(state as CSSState)
91
+ }
92
+
93
+ const cssStateVariableEntries = Object.entries(cssStateVariables)
94
+
95
+ export function getCSSStateVariable(state: string) {
96
+ if (isCSSState(state)) {
97
+ for (const [variableName, { values, defaultValue }] of cssStateVariableEntries) {
98
+ if (state in values) {
99
+ const value = (values as Record<CSSState, CSSStateValue>)[state]
100
+ return {
101
+ name: variableName,
102
+ value: String(value),
103
+ isDefault: value === defaultValue,
104
+ }
105
+ }
106
+ }
107
+ }
108
+ // Support arbitrary states in the format `state-[foo]` or `state-[foo=bar]`
109
+ const [stateName, stateValue] = state.split('=')
110
+ return { name: stateName!, value: stateValue ?? 'true', isDefault: false }
111
+ }
112
+
113
+ export function getPointerStateSelector(state: 'idle' | 'hover' | 'active') {
114
+ return `:is(${state === 'hover' ? '[data-gds-exposed-pointer=hover], [data-gds-exposed-pointer=active]' : `[data-gds-exposed-pointer=${state}]`}, :not([data-gds-exposed-pointer]):where(${state === 'idle' ? ':not(:hover, :active)' : state === 'hover' ? ':hover' : ':is(:hover:active, [data-gds-active] :active)'}))`
115
+ }
116
+
117
+ export function getFocusStateSelector(visible: boolean, within: boolean) {
118
+ return `${within ? ':has' : ':is'}([data-gds-exposed-focus=true], :not([data-gds-exposed-focus]):where(${visible ? ':focus-visible' : ':focus'}))`
119
+ }
120
+
121
+ export function getCheckedStateSelector(checked: boolean | 'indeterminate') {
122
+ return `:is([data-gds-exposed-checked=${checked === 'indeterminate' ? 'indeterminate' : checked}], :not([data-gds-exposed-checked]):where(${checked !== 'indeterminate' ? `${checked ? ':checked' : ':not(:checked)'}:not([aria-checked], [aria-pressed], [aria-selected]), ` : ''}[aria-checked=${checked === 'indeterminate' ? 'mixed' : checked}], [aria-pressed=${checked === 'indeterminate' ? 'mixed' : checked}]${checked !== 'indeterminate' ? `, [aria-selected=${checked}]` : ''}))`
123
+ }
124
+
125
+ export function getDisabledStateSelector(disabled: boolean | 'read-only') {
126
+ return `:is([data-gds-exposed-disabled=${disabled === 'read-only' ? 'read-only' : disabled}], :not([data-gds-exposed-disabled]):where(${disabled === true ? ':is' : ':not'}(:disabled, [aria-disabled=true])${disabled !== true ? `${disabled === 'read-only' ? ':is' : ':not'}(input:not([type=hidden], [type=range], [type=color], [type=checkbox], [type=radio], [type=button], [type=submit], [type=reset], [type=image]):read-only, textarea:read-only, [aria-readonly=true])` : ''}))`
127
+ }
128
+
129
+ export function getOpenStateSelector(open: boolean) {
130
+ return `${open ? ':is' : ':not'}([data-gds-exposed-open=true], :not([data-gds-exposed-open]):where([open], :popover-open, :open, [aria-expanded=true]))`
131
+ }
132
+
133
+ export function getCurrentStateSelector(current: boolean) {
134
+ return `${current ? ':is' : ':not'}([data-gds-exposed-current=true], :not([data-gds-exposed-current]):where([aria-current]:not([aria-current=''], [aria-current=false])))`
135
+ }
136
+
137
+ export function getBlankStateSelector(blank: boolean) {
138
+ // Should use `:blank` instead of `:placeholder-shown`, but no browser supports it
139
+ return `${blank ? ':is' : ':not'}([data-gds-exposed-blank=true], :not([data-gds-exposed-blank]):where(input:placeholder-shown, textarea:placeholder-shown, :not(input, textarea)[data-placeholder-shown]))`
140
+ }
141
+
142
+ export const cssStateSelectors: Record<CSSState, string> = {
143
+ open: getOpenStateSelector(true),
144
+ 'not-open': getOpenStateSelector(false),
145
+ current: getCurrentStateSelector(true),
146
+ 'not-current': getCurrentStateSelector(false),
147
+ checked: getCheckedStateSelector(true),
148
+ indeterminate: getCheckedStateSelector('indeterminate'),
149
+ unchecked: getCheckedStateSelector(false),
150
+ 'read-only': getDisabledStateSelector('read-only'),
151
+ blank: getBlankStateSelector(true),
152
+ 'not-blank': getBlankStateSelector(false),
153
+ idle: getPointerStateSelector('idle'),
154
+ hover: getPointerStateSelector('hover'),
155
+ focus: getFocusStateSelector(true, false),
156
+ 'not-focus': wrapSelector(':not', getFocusStateSelector(true, false)),
157
+ active: getPointerStateSelector('active'),
158
+ enabled: getDisabledStateSelector(false),
159
+ disabled: getDisabledStateSelector(true),
160
+ }