-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinputs.html
More file actions
328 lines (314 loc) · 28.2 KB
/
Copy pathinputs.html
File metadata and controls
328 lines (314 loc) · 28.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Inputs — form-styles</title>
<meta name="description" content="Special input types for forms: date, number, phone, file upload, select, radio, checkbox.">
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
@custom-variant dark (&:where(.dark, .dark *));
@theme {
--color-ink: #1a1a2e;
--color-paper: #fafafa;
--color-brand-50: #eef2ff;
--color-brand-500: #4f46e5;
--color-brand-600: #4338ca;
}
</style>
<script>
// Applied before paint, from a plain <script> (not the "text/tailwindcss" block above, which
// only ever holds CSS, never runs as JS) so the toggle below reflects a saved choice on load
// instead of flashing light mode for a moment first.
if (localStorage.getItem('theme') === 'dark') document.documentElement.classList.add('dark')
</script>
<link href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet">
<style>
pre[class*="language-"] { border-radius: 0; font-size: 0.8125rem; }
</style>
</head>
<body class="bg-paper text-ink antialiased dark:bg-ink dark:text-paper">
<nav class="sticky top-0 z-10 border-b border-ink/10 bg-paper/90 backdrop-blur dark:border-white/10 dark:bg-ink/90">
<div class="mx-auto flex max-w-5xl items-center justify-between px-6 py-4">
<a href="index.html" class="font-bold tracking-tight">form-styles</a>
<div class="flex items-center gap-5 text-sm font-medium">
<a href="inputs.html" class="hover:text-brand-600">Inputs</a>
<a href="states.html" class="hover:text-brand-600">States</a>
<a href="layouts.html" class="hover:text-brand-600">Layouts</a>
<a href="sections.html" class="hover:text-brand-600">Sections</a>
<a href="https://github.com/BootForm/form-styles" class="hover:text-brand-600">GitHub</a>
<button id="theme-toggle" type="button" aria-label="Toggle dark mode"
class="rounded-md border border-ink/10 px-2 py-1 text-xs dark:border-white/20">
<span class="dark:hidden">Dark</span>
<span class="hidden dark:inline">Light</span>
</button>
</div>
</div>
</nav>
<main class="mx-auto max-w-3xl px-6 py-12">
<h1 class="text-3xl font-bold tracking-tight">Inputs</h1>
<p class="mt-3 text-ink/70 dark:text-white/70">Special input types a plain <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em]"><input></code> alone usually looks wrong for.</p>
<section id="baseline" class="scroll-mt-24 border-b border-ink/10 py-10 first:pt-0 last:border-0 dark:border-white/10">
<h2 class="text-xl font-bold tracking-tight">Baseline</h2>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70">The baseline every other input on this page starts from: an explicit border color (not an opacity-based one like <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">border-black/15</code>, which reads as invisible against most backgrounds), a visible focus ring, and an explicit background so dark mode doesn't leave the field blending into the page behind it.</p>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70">Swap <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">brand-500</code>/<code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">brand-600</code> for whatever primary color the site actually defines; every example below assumes those two utilities exist.</p>
<div class="not-prose mt-4 overflow-hidden rounded-xl border border-ink/10 dark:border-white/10">
<div class="flex flex-wrap items-center justify-center gap-4 bg-white p-6 dark:bg-white/[0.03]">
<div class="flex flex-col gap-1">
<label for="name" class="text-sm font-medium">Your name</label>
<input id="name" name="name" type="text" required
class="rounded-md border border-gray-300 bg-white px-3 py-2 outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25 dark:border-gray-600 dark:bg-white/5">
</div>
</div>
<div class="code-block relative">
<button type="button" class="copy-btn absolute right-3 top-3 rounded border border-white/20 bg-white/10 px-2 py-1 text-xs font-medium text-white hover:bg-white/20">Copy</button>
<pre class="!m-0 !rounded-none"><code class="language-markup"><div class="flex flex-col gap-1">
<label for="name" class="text-sm font-medium">Your name</label>
<input id="name" name="name" type="text" required
class="rounded-md border border-gray-300 bg-white px-3 py-2 outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25 dark:border-gray-600 dark:bg-white/5">
</div></code></pre>
</div>
</div>
</section>
<section id="date" class="scroll-mt-24 border-b border-ink/10 py-10 first:pt-0 last:border-0 dark:border-white/10">
<h2 class="text-xl font-bold tracking-tight">Date</h2>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70">A native <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10"><input type="date"></code> picks up this exact same styling; no JavaScript date-picker library needed unless the design calls for a range picker or a non-native calendar UI:</p>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70"><code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">dark:[color-scheme:dark]</code> fixes the native calendar icon and popup rendering as black-on-black in dark mode; without it, the picker itself still uses light-mode colors even though the field around it doesn't.</p>
<div class="not-prose mt-4 overflow-hidden rounded-xl border border-ink/10 dark:border-white/10">
<div class="flex flex-wrap items-center justify-center gap-4 bg-white p-6 dark:bg-white/[0.03]">
<div class="flex flex-col gap-1">
<label for="move-in" class="text-sm font-medium">Preferred date</label>
<input id="move-in" name="move_in_date" type="date" required
class="rounded-md border border-gray-300 bg-white px-3 py-2 outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25 dark:border-gray-600 dark:bg-white/5 dark:[color-scheme:dark]">
</div>
</div>
<div class="code-block relative">
<button type="button" class="copy-btn absolute right-3 top-3 rounded border border-white/20 bg-white/10 px-2 py-1 text-xs font-medium text-white hover:bg-white/20">Copy</button>
<pre class="!m-0 !rounded-none"><code class="language-markup"><div class="flex flex-col gap-1">
<label for="move-in" class="text-sm font-medium">Preferred date</label>
<input id="move-in" name="move_in_date" type="date" required
class="rounded-md border border-gray-300 bg-white px-3 py-2 outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25 dark:border-gray-600 dark:bg-white/5 dark:[color-scheme:dark]">
</div></code></pre>
</div>
</div>
</section>
<section id="number" class="scroll-mt-24 border-b border-ink/10 py-10 first:pt-0 last:border-0 dark:border-white/10">
<h2 class="text-xl font-bold tracking-tight">Number</h2>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70">A quantity or count field. Same base styling, plus <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">min</code>/<code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">max</code>/<code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">step</code> for real validation instead of just a visual hint:</p>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70"><code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">w-24</code> instead of a full-width field: a number this short doesn't need the same width as an email field, and a full-width number input reads oddly.</p>
<div class="not-prose mt-4 overflow-hidden rounded-xl border border-ink/10 dark:border-white/10">
<div class="flex flex-wrap items-center justify-center gap-4 bg-white p-6 dark:bg-white/[0.03]">
<div class="flex flex-col gap-1">
<label for="guests" class="text-sm font-medium">Number of guests</label>
<input id="guests" name="guests" type="number" min="1" max="20" step="1" required
class="w-24 rounded-md border border-gray-300 bg-white px-3 py-2 outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25 dark:border-gray-600 dark:bg-white/5">
</div>
</div>
<div class="code-block relative">
<button type="button" class="copy-btn absolute right-3 top-3 rounded border border-white/20 bg-white/10 px-2 py-1 text-xs font-medium text-white hover:bg-white/20">Copy</button>
<pre class="!m-0 !rounded-none"><code class="language-markup"><div class="flex flex-col gap-1">
<label for="guests" class="text-sm font-medium">Number of guests</label>
<input id="guests" name="guests" type="number" min="1" max="20" step="1" required
class="w-24 rounded-md border border-gray-300 bg-white px-3 py-2 outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25 dark:border-gray-600 dark:bg-white/5">
</div></code></pre>
</div>
</div>
</section>
<section id="phone" class="scroll-mt-24 border-b border-ink/10 py-10 first:pt-0 last:border-0 dark:border-white/10">
<h2 class="text-xl font-bold tracking-tight">Phone</h2>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70">A <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">tel</code> input, not <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">text</code>, gets phone-appropriate mobile keyboards for free and lets <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">pattern</code> validate a rough shape without a JavaScript library:</p>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70">A separate country-code <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10"><select></code> next to this field is a common upgrade; skip it unless the site actually serves multiple countries; it's one more required decision for most visitors.</p>
<div class="not-prose mt-4 overflow-hidden rounded-xl border border-ink/10 dark:border-white/10">
<div class="flex flex-wrap items-center justify-center gap-4 bg-white p-6 dark:bg-white/[0.03]">
<div class="flex flex-col gap-1">
<label for="phone" class="text-sm font-medium">Phone number</label>
<input id="phone" name="phone" type="tel" autocomplete="tel"
pattern="[0-9+()\-\s]{7,}" placeholder="+1 (555) 555-0123"
class="rounded-md border border-gray-300 bg-white px-3 py-2 outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25 dark:border-gray-600 dark:bg-white/5">
</div>
</div>
<div class="code-block relative">
<button type="button" class="copy-btn absolute right-3 top-3 rounded border border-white/20 bg-white/10 px-2 py-1 text-xs font-medium text-white hover:bg-white/20">Copy</button>
<pre class="!m-0 !rounded-none"><code class="language-markup"><div class="flex flex-col gap-1">
<label for="phone" class="text-sm font-medium">Phone number</label>
<input id="phone" name="phone" type="tel" autocomplete="tel"
pattern="[0-9+()\-\s]{7,}" placeholder="+1 (555) 555-0123"
class="rounded-md border border-gray-300 bg-white px-3 py-2 outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25 dark:border-gray-600 dark:bg-white/5">
</div></code></pre>
</div>
</div>
</section>
<section id="file-upload" class="scroll-mt-24 border-b border-ink/10 py-10 first:pt-0 last:border-0 dark:border-white/10">
<h2 class="text-xl font-bold tracking-tight">File upload</h2>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70">Native <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10"><input type="file"></code> is nearly impossible to style directly across browsers; the reliable pattern is hiding it and styling a <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10"><label></code> that triggers it instead:</p>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70"><code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">sr-only</code> (a standard Tailwind utility: visually hidden, still in the accessibility tree and still focusable/keyboard-operable) rather than <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">hidden</code>, which would also remove it from tab order and break keyboard users entirely.</p>
<div class="not-prose mt-4 overflow-hidden rounded-xl border border-ink/10 dark:border-white/10">
<div class="flex flex-wrap items-center justify-center gap-4 bg-white p-6 dark:bg-white/[0.03]">
<div class="flex flex-col gap-1">
<span class="text-sm font-medium">Resume</span>
<label for="resume" class="flex w-fit cursor-pointer items-center gap-2 rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium hover:bg-gray-50 dark:border-gray-600 dark:bg-white/5 dark:hover:bg-white/10">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-4"><path d="M9.25 13.25a.75.75 0 0 0 1.5 0V4.66l2.1 2.1a.75.75 0 1 0 1.06-1.06l-3.38-3.38a.75.75 0 0 0-1.06 0L6.09 5.7a.75.75 0 0 0 1.06 1.06l2.1-2.1v8.59Z"/><path d="M3.5 12.75a.75.75 0 0 0-1.5 0v2.5A2.75 2.75 0 0 0 4.75 18h10.5A2.75 2.75 0 0 0 18 15.25v-2.5a.75.75 0 0 0-1.5 0v2.5c0 .69-.56 1.25-1.25 1.25H4.75c-.69 0-1.25-.56-1.25-1.25v-2.5Z"/></svg>
<span id="resume-filename">Choose a file</span>
</label>
<input id="resume" name="resume" type="file" required class="sr-only">
</div>
<script>
document.getElementById('resume').addEventListener('change', (event) => {
document.getElementById('resume-filename').textContent = event.target.files[0]?.name ?? 'Choose a file'
})
</script>
</div>
<div class="code-block relative">
<button type="button" class="copy-btn absolute right-3 top-3 rounded border border-white/20 bg-white/10 px-2 py-1 text-xs font-medium text-white hover:bg-white/20">Copy</button>
<pre class="!m-0 !rounded-none"><code class="language-markup"><div class="flex flex-col gap-1">
<span class="text-sm font-medium">Resume</span>
<label for="resume" class="flex w-fit cursor-pointer items-center gap-2 rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium hover:bg-gray-50 dark:border-gray-600 dark:bg-white/5 dark:hover:bg-white/10">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-4"><path d="M9.25 13.25a.75.75 0 0 0 1.5 0V4.66l2.1 2.1a.75.75 0 1 0 1.06-1.06l-3.38-3.38a.75.75 0 0 0-1.06 0L6.09 5.7a.75.75 0 0 0 1.06 1.06l2.1-2.1v8.59Z"/><path d="M3.5 12.75a.75.75 0 0 0-1.5 0v2.5A2.75 2.75 0 0 0 4.75 18h10.5A2.75 2.75 0 0 0 18 15.25v-2.5a.75.75 0 0 0-1.5 0v2.5c0 .69-.56 1.25-1.25 1.25H4.75c-.69 0-1.25-.56-1.25-1.25v-2.5Z"/></svg>
<span id="resume-filename">Choose a file</span>
</label>
<input id="resume" name="resume" type="file" required class="sr-only">
</div>
<script>
document.getElementById('resume').addEventListener('change', (event) => {
document.getElementById('resume-filename').textContent = event.target.files[0]?.name ?? 'Choose a file'
})
</script></code></pre>
</div>
</div>
</section>
<section id="select" class="scroll-mt-24 border-b border-ink/10 py-10 first:pt-0 last:border-0 dark:border-white/10">
<h2 class="text-xl font-bold tracking-tight">Select</h2>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70">A native <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10"><select></code>'s dropdown popup follows the browser/OS's own light-or-dark preference by default, not the page's, which can leave its options rendering as invisible white-on-white (or black-on-black) until you hover one. <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">[color-scheme:light]</code> / <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">dark:[color-scheme:dark]</code> tells the browser explicitly which native palette to use for the parts of the control it renders itself, the same fix the date input above needs for its calendar popup. <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">color-scheme</code> alone wasn't reliable enough on its own in testing (some Chrome versions still rendered the popup with default colors regardless), so each <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10"><option></code> also sets its own <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">background-color</code>/<code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">color</code> directly, which Chrome does honor for the popup list even though it ignores most other CSS on <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10"><option></code>.</p>
<div class="not-prose mt-4 overflow-hidden rounded-xl border border-ink/10 dark:border-white/10">
<div class="flex flex-wrap items-center justify-center gap-4 bg-white p-6 dark:bg-white/[0.03]">
<div class="flex flex-col gap-1">
<label for="budget" class="text-sm font-medium">Budget range</label>
<select id="budget" name="budget" required
class="rounded-md border border-gray-300 bg-white px-3 py-2 outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25 [color-scheme:light] dark:border-gray-600 dark:bg-white/5 dark:[color-scheme:dark]">
<option value="" disabled selected class="bg-white text-ink dark:bg-ink dark:text-paper">Choose one</option>
<option value="under-1k" class="bg-white text-ink dark:bg-ink dark:text-paper">Under $1,000</option>
<option value="1k-5k" class="bg-white text-ink dark:bg-ink dark:text-paper">$1,000 - $5,000</option>
<option value="5k-plus" class="bg-white text-ink dark:bg-ink dark:text-paper">$5,000+</option>
</select>
</div>
</div>
<div class="code-block relative">
<button type="button" class="copy-btn absolute right-3 top-3 rounded border border-white/20 bg-white/10 px-2 py-1 text-xs font-medium text-white hover:bg-white/20">Copy</button>
<pre class="!m-0 !rounded-none"><code class="language-markup"><div class="flex flex-col gap-1">
<label for="budget" class="text-sm font-medium">Budget range</label>
<select id="budget" name="budget" required
class="rounded-md border border-gray-300 bg-white px-3 py-2 outline-none focus:border-brand-500 focus:ring-2 focus:ring-brand-500/25 [color-scheme:light] dark:border-gray-600 dark:bg-white/5 dark:[color-scheme:dark]">
<option value="" disabled selected class="bg-white text-ink dark:bg-ink dark:text-paper">Choose one</option>
<option value="under-1k" class="bg-white text-ink dark:bg-ink dark:text-paper">Under $1,000</option>
<option value="1k-5k" class="bg-white text-ink dark:bg-ink dark:text-paper">$1,000 - $5,000</option>
<option value="5k-plus" class="bg-white text-ink dark:bg-ink dark:text-paper">$5,000+</option>
</select>
</div></code></pre>
</div>
</div>
</section>
<section id="radio-group-segmented-control-look" class="scroll-mt-24 border-b border-ink/10 py-10 first:pt-0 last:border-0 dark:border-white/10">
<h2 class="text-xl font-bold tracking-tight">Radio group (segmented control look)</h2>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70">A styled radio group reads as a set of buttons instead of the browser's default small circles, using <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">peer</code> and <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">:has()</code> so no JavaScript is needed to toggle the selected look:</p>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70"><code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">has-[:checked]:</code> needs Tailwind v3.4+ (the <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">:has()</code> variant); on an older Tailwind, style the sibling <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10"><input></code>'s <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">checked</code> state with <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">peer-checked:</code> on an adjacent element instead.</p>
<div class="not-prose mt-4 overflow-hidden rounded-xl border border-ink/10 dark:border-white/10">
<div class="flex flex-wrap items-center justify-center gap-4 bg-white p-6 dark:bg-white/[0.03]">
<fieldset class="flex flex-col gap-1">
<legend class="text-sm font-medium">Project type</legend>
<div class="flex gap-2">
<label class="has-[:checked]:border-brand-500 has-[:checked]:bg-brand-50 has-[:checked]:text-brand-600 dark:has-[:checked]:bg-brand-500/20 dark:has-[:checked]:text-brand-500 flex-1 cursor-pointer rounded-md border border-gray-300 px-4 py-2 text-center text-sm font-medium dark:border-gray-600">
<input type="radio" name="project_type" value="new" class="sr-only" checked>
New site
</label>
<label class="has-[:checked]:border-brand-500 has-[:checked]:bg-brand-50 has-[:checked]:text-brand-600 dark:has-[:checked]:bg-brand-500/20 dark:has-[:checked]:text-brand-500 flex-1 cursor-pointer rounded-md border border-gray-300 px-4 py-2 text-center text-sm font-medium dark:border-gray-600">
<input type="radio" name="project_type" value="redesign" class="sr-only">
Redesign
</label>
</div>
</fieldset>
</div>
<div class="code-block relative">
<button type="button" class="copy-btn absolute right-3 top-3 rounded border border-white/20 bg-white/10 px-2 py-1 text-xs font-medium text-white hover:bg-white/20">Copy</button>
<pre class="!m-0 !rounded-none"><code class="language-markup"><fieldset class="flex flex-col gap-1">
<legend class="text-sm font-medium">Project type</legend>
<div class="flex gap-2">
<label class="has-[:checked]:border-brand-500 has-[:checked]:bg-brand-50 has-[:checked]:text-brand-600 dark:has-[:checked]:bg-brand-500/20 dark:has-[:checked]:text-brand-500 flex-1 cursor-pointer rounded-md border border-gray-300 px-4 py-2 text-center text-sm font-medium dark:border-gray-600">
<input type="radio" name="project_type" value="new" class="sr-only" checked>
New site
</label>
<label class="has-[:checked]:border-brand-500 has-[:checked]:bg-brand-50 has-[:checked]:text-brand-600 dark:has-[:checked]:bg-brand-500/20 dark:has-[:checked]:text-brand-500 flex-1 cursor-pointer rounded-md border border-gray-300 px-4 py-2 text-center text-sm font-medium dark:border-gray-600">
<input type="radio" name="project_type" value="redesign" class="sr-only">
Redesign
</label>
</div>
</fieldset></code></pre>
</div>
</div>
</section>
<section id="checkbox-group" class="scroll-mt-24 border-b border-ink/10 py-10 first:pt-0 last:border-0 dark:border-white/10">
<h2 class="text-xl font-bold tracking-tight">Checkbox group</h2>
<p class="mt-2 text-sm leading-relaxed text-ink/70 dark:text-white/70"><code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">text-brand-500</code> on a checkbox/radio input sets its own check/dot color, since browsers render native checkbox and radio marks using <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">currentColor</code>/<code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">accent-color</code> under the hood; Tailwind's <code class="rounded bg-ink/5 px-1 py-0.5 text-[0.85em] dark:bg-white/10">text-*</code> utilities are what set that.</p>
<div class="not-prose mt-4 overflow-hidden rounded-xl border border-ink/10 dark:border-white/10">
<div class="flex flex-wrap items-center justify-center gap-4 bg-white p-6 dark:bg-white/[0.03]">
<fieldset class="flex flex-col gap-2">
<legend class="text-sm font-medium">What do you need?</legend>
<label class="flex items-center gap-2 text-sm">
<input type="checkbox" name="needs[]" value="design" class="size-4 rounded border-gray-300 text-brand-500 focus:ring-brand-500/25 dark:border-gray-600">
Design
</label>
<label class="flex items-center gap-2 text-sm">
<input type="checkbox" name="needs[]" value="development" class="size-4 rounded border-gray-300 text-brand-500 focus:ring-brand-500/25 dark:border-gray-600">
Development
</label>
</fieldset>
</div>
<div class="code-block relative">
<button type="button" class="copy-btn absolute right-3 top-3 rounded border border-white/20 bg-white/10 px-2 py-1 text-xs font-medium text-white hover:bg-white/20">Copy</button>
<pre class="!m-0 !rounded-none"><code class="language-markup"><fieldset class="flex flex-col gap-2">
<legend class="text-sm font-medium">What do you need?</legend>
<label class="flex items-center gap-2 text-sm">
<input type="checkbox" name="needs[]" value="design" class="size-4 rounded border-gray-300 text-brand-500 focus:ring-brand-500/25 dark:border-gray-600">
Design
</label>
<label class="flex items-center gap-2 text-sm">
<input type="checkbox" name="needs[]" value="development" class="size-4 rounded border-gray-300 text-brand-500 focus:ring-brand-500/25 dark:border-gray-600">
Development
</label>
</fieldset></code></pre>
</div>
</div>
</section>
</main>
<footer class="border-t border-ink/10 px-6 py-8 text-center text-sm text-ink/50 dark:border-white/10">
Built by <a href="https://bootform.com" class="underline">BootForm</a>. Part of the
<a href="https://github.com/BootForm" class="underline">github.com/BootForm</a> org: free templates
and a working contact form for whatever you build.
</footer>
<script>
document.getElementById('theme-toggle').addEventListener('click', () => {
const isDark = document.documentElement.classList.toggle('dark')
localStorage.setItem('theme', isDark ? 'dark' : 'light')
})
</script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-markup.min.js"></script>
<script>
// A hand-rolled copy button instead of Prism's own copy-to-clipboard plugin: that plugin
// relies on the deprecated document.execCommand('copy'), which modern Chrome frequently
// refuses silently, falling back to a "Press Ctrl+C to copy" message instead of copying
// anything. navigator.clipboard.writeText() is the reliable modern equivalent.
document.querySelectorAll('.copy-btn').forEach((btn) => {
btn.addEventListener('click', async () => {
const code = btn.closest('.code-block').querySelector('code').textContent
try {
await navigator.clipboard.writeText(code)
btn.textContent = 'Copied!'
} catch {
btn.textContent = 'Press Ctrl+C'
}
setTimeout(() => { btn.textContent = 'Copy' }, 1500)
})
})
</script>
</body>
</html>