tailwindcss-forms-style 0.1.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/src/index.js ADDED
@@ -0,0 +1,368 @@
1
+ const svgToDataUri = require('mini-svg-data-uri')
2
+ const plugin = require('tailwindcss/plugin')
3
+ const defaultTheme = require('tailwindcss/defaultTheme')
4
+ const colors = require('tailwindcss/colors')
5
+ const [baseFontSize, { lineHeight: baseLineHeight }] = defaultTheme.fontSize.base
6
+ const { spacing, borderWidth, borderRadius } = defaultTheme
7
+
8
+ function resolveColor(color, opacityVariableName) {
9
+ return color.replace('<alpha-value>', `var(${opacityVariableName}, 1)`)
10
+ }
11
+
12
+ const forms = plugin.withOptions(function (options = { strategy: undefined }) {
13
+ return function ({ addBase, addComponents, theme }) {
14
+ function resolveChevronColor(color, fallback) {
15
+ let resolved = theme(color)
16
+
17
+ if (!resolved || resolved.includes('var(')) {
18
+ return fallback
19
+ }
20
+
21
+ return resolved.replace('<alpha-value>', '1')
22
+ }
23
+
24
+ const strategy = options.strategy === undefined ? ['base', 'class'] : [options.strategy]
25
+
26
+ const rules = [
27
+ {
28
+ base: [
29
+ "input:where([type='text'])",
30
+ 'input:where(:not([type]))',
31
+ "input:where([type='email'])",
32
+ "input:where([type='url'])",
33
+ "input:where([type='password'])",
34
+ "input:where([type='number'])",
35
+ "input:where([type='date'])",
36
+ "input:where([type='datetime-local'])",
37
+ "input:where([type='month'])",
38
+ "input:where([type='search'])",
39
+ "input:where([type='tel'])",
40
+ "input:where([type='time'])",
41
+ "input:where([type='week'])",
42
+ 'select:where([multiple])',
43
+ 'textarea',
44
+ 'select',
45
+ ],
46
+ class: ['.form-input', '.form-textarea', '.form-select', '.form-multiselect'],
47
+ styles: {
48
+ appearance: 'none',
49
+ 'background-color': '#fff',
50
+ 'border-color': resolveColor(
51
+ theme('colors.gray.500', colors.gray[500]),
52
+ '--tw-border-opacity'
53
+ ),
54
+ 'border-width': borderWidth['DEFAULT'],
55
+ 'border-radius': borderRadius.none,
56
+ 'padding-top': spacing[2],
57
+ 'padding-right': spacing[3],
58
+ 'padding-bottom': spacing[2],
59
+ 'padding-left': spacing[3],
60
+ 'font-size': baseFontSize,
61
+ 'line-height': baseLineHeight,
62
+ '--tw-shadow': '0 0 #0000',
63
+ '&:focus': {
64
+ outline: '2px solid transparent',
65
+ 'outline-offset': '2px',
66
+ '--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)',
67
+ '--tw-ring-offset-width': '0px',
68
+ '--tw-ring-offset-color': '#fff',
69
+ '--tw-ring-color': resolveColor(
70
+ theme('colors.blue.600', colors.blue[600]),
71
+ '--tw-ring-opacity'
72
+ ),
73
+ '--tw-ring-offset-shadow': `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`,
74
+ '--tw-ring-shadow': `var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)`,
75
+ 'box-shadow': `var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)`,
76
+ 'border-color': resolveColor(
77
+ theme('colors.blue.600', colors.blue[600]),
78
+ '--tw-border-opacity'
79
+ ),
80
+ },
81
+ },
82
+ },
83
+ {
84
+ base: ['input::placeholder', 'textarea::placeholder'],
85
+ class: ['.form-input::placeholder', '.form-textarea::placeholder'],
86
+ styles: {
87
+ color: resolveColor(theme('colors.gray.500', colors.gray[500]), '--tw-text-opacity'),
88
+ opacity: '1',
89
+ },
90
+ },
91
+ {
92
+ base: ['::-webkit-datetime-edit-fields-wrapper'],
93
+ class: ['.form-input::-webkit-datetime-edit-fields-wrapper'],
94
+ styles: {
95
+ padding: '0',
96
+ },
97
+ },
98
+ {
99
+ // Unfortunate hack until https://bugs.webkit.org/show_bug.cgi?id=198959 is fixed.
100
+ // This sucks because users can't change line-height with a utility on date inputs now.
101
+ // Reference: https://github.com/twbs/bootstrap/pull/31993
102
+ base: ['::-webkit-date-and-time-value'],
103
+ class: ['.form-input::-webkit-date-and-time-value'],
104
+ styles: {
105
+ 'min-height': '1.5em',
106
+ },
107
+ },
108
+ {
109
+ // In Safari on iOS date and time inputs are centered instead of left-aligned and can't be
110
+ // changed with `text-align` utilities on the input by default. Resetting this to `inherit`
111
+ // makes them left-aligned by default and makes it possible to override the alignment with
112
+ // utility classes without using an arbitrary variant to target the pseudo-elements.
113
+ base: ['::-webkit-date-and-time-value'],
114
+ class: ['.form-input::-webkit-date-and-time-value'],
115
+ styles: {
116
+ 'text-align': 'inherit',
117
+ },
118
+ },
119
+ {
120
+ // In Safari on macOS date time inputs that are set to `display: block` have unexpected
121
+ // extra bottom spacing. This can be corrected by setting the `::-webkit-datetime-edit`
122
+ // pseudo-element to `display: inline-flex`, instead of the browser default of
123
+ // `display: inline-block`.
124
+ base: ['::-webkit-datetime-edit'],
125
+ class: ['.form-input::-webkit-datetime-edit'],
126
+ styles: {
127
+ display: 'inline-flex',
128
+ },
129
+ },
130
+ {
131
+ // In Safari on macOS date time inputs are 4px taller than normal inputs
132
+ // This is because there is extra padding on the datetime-edit and datetime-edit-{part}-field pseudo elements
133
+ // See https://github.com/tailwindlabs/tailwindcss-forms/issues/95
134
+ base: [
135
+ '::-webkit-datetime-edit',
136
+ '::-webkit-datetime-edit-year-field',
137
+ '::-webkit-datetime-edit-month-field',
138
+ '::-webkit-datetime-edit-day-field',
139
+ '::-webkit-datetime-edit-hour-field',
140
+ '::-webkit-datetime-edit-minute-field',
141
+ '::-webkit-datetime-edit-second-field',
142
+ '::-webkit-datetime-edit-millisecond-field',
143
+ '::-webkit-datetime-edit-meridiem-field',
144
+ ],
145
+ class: [
146
+ '.form-input::-webkit-datetime-edit',
147
+ '.form-input::-webkit-datetime-edit-year-field',
148
+ '.form-input::-webkit-datetime-edit-month-field',
149
+ '.form-input::-webkit-datetime-edit-day-field',
150
+ '.form-input::-webkit-datetime-edit-hour-field',
151
+ '.form-input::-webkit-datetime-edit-minute-field',
152
+ '.form-input::-webkit-datetime-edit-second-field',
153
+ '.form-input::-webkit-datetime-edit-millisecond-field',
154
+ '.form-input::-webkit-datetime-edit-meridiem-field',
155
+ ],
156
+ styles: {
157
+ 'padding-top': 0,
158
+ 'padding-bottom': 0,
159
+ },
160
+ },
161
+ {
162
+ base: ['select'],
163
+ class: ['.form-select'],
164
+ styles: {
165
+ 'background-image': `url("${svgToDataUri(
166
+ `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"><path stroke="${resolveChevronColor(
167
+ 'colors.gray.500',
168
+ colors.gray[500]
169
+ )}" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 8l4 4 4-4"/></svg>`
170
+ )}")`,
171
+ 'background-position': `right ${spacing[2]} center`,
172
+ 'background-repeat': `no-repeat`,
173
+ 'background-size': `1.5em 1.5em`,
174
+ 'padding-right': spacing[10],
175
+ 'print-color-adjust': `exact`,
176
+ },
177
+ },
178
+ {
179
+ base: ['select:where([multiple])', 'select:where([size]:not([size="1"]))'],
180
+ class: ['.form-select:where([size]:not([size="1"]))'],
181
+ styles: {
182
+ 'background-image': 'initial',
183
+ 'background-position': 'initial',
184
+ 'background-repeat': 'unset',
185
+ 'background-size': 'initial',
186
+ 'padding-right': spacing[3],
187
+ 'print-color-adjust': 'unset',
188
+ },
189
+ },
190
+ {
191
+ base: [`input:where([type='checkbox'])`, `input:where([type='radio'])`],
192
+ class: ['.form-checkbox', '.form-radio'],
193
+ styles: {
194
+ appearance: 'none',
195
+ padding: '0',
196
+ 'print-color-adjust': 'exact',
197
+ display: 'inline-block',
198
+ 'vertical-align': 'middle',
199
+ 'background-origin': 'border-box',
200
+ 'user-select': 'none',
201
+ 'flex-shrink': '0',
202
+ height: spacing[4],
203
+ width: spacing[4],
204
+ color: resolveColor(theme('colors.blue.600', colors.blue[600]), '--tw-text-opacity'),
205
+ 'background-color': '#fff',
206
+ 'border-color': resolveColor(
207
+ theme('colors.gray.500', colors.gray[500]),
208
+ '--tw-border-opacity'
209
+ ),
210
+ 'border-width': borderWidth['DEFAULT'],
211
+ '--tw-shadow': '0 0 #0000',
212
+ },
213
+ },
214
+ {
215
+ base: [`input:where([type='checkbox'])`],
216
+ class: ['.form-checkbox'],
217
+ styles: {
218
+ 'border-radius': borderRadius['none'],
219
+ },
220
+ },
221
+ {
222
+ base: [`input:where([type='radio'])`],
223
+ class: ['.form-radio'],
224
+ styles: {
225
+ 'border-radius': '100%',
226
+ },
227
+ },
228
+ {
229
+ base: [`input:where([type='checkbox']):focus`, `input:where([type='radio']):focus`],
230
+ class: ['.form-checkbox:focus', '.form-radio:focus'],
231
+ styles: {
232
+ outline: '2px solid transparent',
233
+ 'outline-offset': '2px',
234
+ '--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)',
235
+ '--tw-ring-offset-width': '2px',
236
+ '--tw-ring-offset-color': '#fff',
237
+ '--tw-ring-color': resolveColor(
238
+ theme('colors.blue.600', colors.blue[600]),
239
+ '--tw-ring-opacity'
240
+ ),
241
+ '--tw-ring-offset-shadow': `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`,
242
+ '--tw-ring-shadow': `var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)`,
243
+ 'box-shadow': `var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)`,
244
+ },
245
+ },
246
+ {
247
+ base: [`input:where([type='checkbox']):checked`, `input:where([type='radio']):checked`],
248
+ class: ['.form-checkbox:checked', '.form-radio:checked'],
249
+ styles: {
250
+ 'border-color': `transparent`,
251
+ 'background-color': `currentColor`,
252
+ 'background-size': `100% 100%`,
253
+ 'background-position': `center`,
254
+ 'background-repeat': `no-repeat`,
255
+ },
256
+ },
257
+ {
258
+ base: [`input:where([type='checkbox']):checked`],
259
+ class: ['.form-checkbox:checked'],
260
+ styles: {
261
+ 'background-image': `url("${svgToDataUri(
262
+ `<svg viewBox="0 0 16 16" fill="white" xmlns="http://www.w3.org/2000/svg"><path d="M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z"/></svg>`
263
+ )}")`,
264
+
265
+ '@media (forced-colors: active) ': {
266
+ appearance: 'auto',
267
+ },
268
+ },
269
+ },
270
+ {
271
+ base: [`input:where([type='radio']):checked`],
272
+ class: ['.form-radio:checked'],
273
+ styles: {
274
+ 'background-image': `url("${svgToDataUri(
275
+ `<svg viewBox="0 0 16 16" fill="white" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" r="3"/></svg>`
276
+ )}")`,
277
+
278
+ '@media (forced-colors: active) ': {
279
+ appearance: 'auto',
280
+ },
281
+ },
282
+ },
283
+ {
284
+ base: [
285
+ `input:where([type='checkbox']):checked:hover`,
286
+ `input:where([type='checkbox']):checked:focus`,
287
+ `input:where([type='radio']):checked:hover`,
288
+ `input:where([type='radio']):checked:focus`,
289
+ ],
290
+ class: [
291
+ '.form-checkbox:checked:hover',
292
+ '.form-checkbox:checked:focus',
293
+ '.form-radio:checked:hover',
294
+ '.form-radio:checked:focus',
295
+ ],
296
+ styles: {
297
+ 'border-color': 'transparent',
298
+ 'background-color': 'currentColor',
299
+ },
300
+ },
301
+ {
302
+ base: [`input:where([type='checkbox']):indeterminate`],
303
+ class: ['.form-checkbox:indeterminate'],
304
+ styles: {
305
+ 'background-image': `url("${svgToDataUri(
306
+ `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16"><path stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h8"/></svg>`
307
+ )}")`,
308
+ 'border-color': `transparent`,
309
+ 'background-color': `currentColor`,
310
+ 'background-size': `100% 100%`,
311
+ 'background-position': `center`,
312
+ 'background-repeat': `no-repeat`,
313
+
314
+ '@media (forced-colors: active) ': {
315
+ appearance: 'auto',
316
+ },
317
+ },
318
+ },
319
+ {
320
+ base: [`input:where([type='checkbox']):indeterminate:hover`, `input:where([type='checkbox']):indeterminate:focus`],
321
+ class: ['.form-checkbox:indeterminate:hover', '.form-checkbox:indeterminate:focus'],
322
+ styles: {
323
+ 'border-color': 'transparent',
324
+ 'background-color': 'currentColor',
325
+ },
326
+ },
327
+ {
328
+ base: [`input:where([type='file'])`],
329
+ class: null,
330
+ styles: {
331
+ background: 'unset',
332
+ 'border-color': 'inherit',
333
+ 'border-width': '0',
334
+ 'border-radius': '0',
335
+ padding: '0',
336
+ 'font-size': 'unset',
337
+ 'line-height': 'inherit',
338
+ },
339
+ },
340
+ {
341
+ base: [`input:where([type='file']):focus`],
342
+ class: null,
343
+ styles: {
344
+ outline: [`1px solid ButtonText`, `1px auto -webkit-focus-ring-color`],
345
+ },
346
+ },
347
+ ]
348
+
349
+ const getStrategyRules = (strategy) =>
350
+ rules
351
+ .map((rule) => {
352
+ if (rule[strategy] === null) return null
353
+
354
+ return { [rule[strategy]]: rule.styles }
355
+ })
356
+ .filter(Boolean)
357
+
358
+ if (strategy.includes('base')) {
359
+ addBase(getStrategyRules('base'))
360
+ }
361
+
362
+ if (strategy.includes('class')) {
363
+ addComponents(getStrategyRules('class'))
364
+ }
365
+ }
366
+ })
367
+
368
+ module.exports = forms
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ content: ['./index.html', './kitchen-sink.html'],
3
+ theme: {
4
+ extend: {
5
+ //
6
+ },
7
+ },
8
+ plugins: [require('./src')],
9
+ }