@pokle/basecoat 0.3.10-beta2.pokle

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 (145) hide show
  1. package/.eleventy.js +47 -0
  2. package/.gitattributes +1 -0
  3. package/.gitignore +12 -0
  4. package/AGENTS.md +112 -0
  5. package/CONTRIBUTING.md +15 -0
  6. package/LICENSE.md +21 -0
  7. package/README.md +29 -0
  8. package/ROADMAP.md +20 -0
  9. package/docs/css/custom.css +92 -0
  10. package/docs/css/highlight.css +151 -0
  11. package/docs/css/styles.css +8 -0
  12. package/docs/css/themes/claude.css +95 -0
  13. package/docs/css/themes/doom-64.css +95 -0
  14. package/docs/css/themes/supabase.css +100 -0
  15. package/docs/src/_data/site.js +10 -0
  16. package/docs/src/_includes/layouts/base.njk +112 -0
  17. package/docs/src/_includes/layouts/layout.njk +15 -0
  18. package/docs/src/_includes/layouts/page.njk +21 -0
  19. package/docs/src/_includes/macros/code_block.njk +23 -0
  20. package/docs/src/_includes/macros/code_preview.njk +26 -0
  21. package/docs/src/_includes/macros/toc.njk +20 -0
  22. package/docs/src/_includes/partials/header.njk +64 -0
  23. package/docs/src/_includes/partials/kitchen-sink/accordion.njk +89 -0
  24. package/docs/src/_includes/partials/kitchen-sink/alert-dialog.njk +26 -0
  25. package/docs/src/_includes/partials/kitchen-sink/alert.njk +72 -0
  26. package/docs/src/_includes/partials/kitchen-sink/avatar.njk +37 -0
  27. package/docs/src/_includes/partials/kitchen-sink/badge.njk +47 -0
  28. package/docs/src/_includes/partials/kitchen-sink/breadcrumb.njk +42 -0
  29. package/docs/src/_includes/partials/kitchen-sink/button.njk +101 -0
  30. package/docs/src/_includes/partials/kitchen-sink/card.njk +147 -0
  31. package/docs/src/_includes/partials/kitchen-sink/checkbox.njk +34 -0
  32. package/docs/src/_includes/partials/kitchen-sink/combobox.njk +83 -0
  33. package/docs/src/_includes/partials/kitchen-sink/dialog.njk +65 -0
  34. package/docs/src/_includes/partials/kitchen-sink/dropdown-menu.njk +294 -0
  35. package/docs/src/_includes/partials/kitchen-sink/form.njk +106 -0
  36. package/docs/src/_includes/partials/kitchen-sink/input.njk +27 -0
  37. package/docs/src/_includes/partials/kitchen-sink/label.njk +30 -0
  38. package/docs/src/_includes/partials/kitchen-sink/pagination.njk +34 -0
  39. package/docs/src/_includes/partials/kitchen-sink/popover.njk +43 -0
  40. package/docs/src/_includes/partials/kitchen-sink/radio-group.njk +33 -0
  41. package/docs/src/_includes/partials/kitchen-sink/select.njk +99 -0
  42. package/docs/src/_includes/partials/kitchen-sink/skeleton.njk +40 -0
  43. package/docs/src/_includes/partials/kitchen-sink/slider.njk +38 -0
  44. package/docs/src/_includes/partials/kitchen-sink/switch.njk +27 -0
  45. package/docs/src/_includes/partials/kitchen-sink/table.njk +74 -0
  46. package/docs/src/_includes/partials/kitchen-sink/tabs.njk +105 -0
  47. package/docs/src/_includes/partials/kitchen-sink/textarea.njk +27 -0
  48. package/docs/src/_includes/partials/kitchen-sink/toast.njk +25 -0
  49. package/docs/src/_includes/partials/kitchen-sink/tooltip.njk +24 -0
  50. package/docs/src/_includes/partials/sidebar.njk +139 -0
  51. package/docs/src/assets/apple-touch-icon.png +0 -0
  52. package/docs/src/assets/favicon.svg +12 -0
  53. package/docs/src/assets/images/avatar-1.png +0 -0
  54. package/docs/src/assets/images/avatar-2.png +0 -0
  55. package/docs/src/assets/images/avatar-3.png +0 -0
  56. package/docs/src/assets/images/screenshot.png +0 -0
  57. package/docs/src/assets/social-screenshot.png +0 -0
  58. package/docs/src/assets/social.png +0 -0
  59. package/docs/src/assets/styles.css +6309 -0
  60. package/docs/src/components/accordion.njk +75 -0
  61. package/docs/src/components/alert-dialog.njk +119 -0
  62. package/docs/src/components/alert.njk +108 -0
  63. package/docs/src/components/avatar.njk +40 -0
  64. package/docs/src/components/badge.njk +93 -0
  65. package/docs/src/components/breadcrumb.njk +71 -0
  66. package/docs/src/components/button-group.njk +290 -0
  67. package/docs/src/components/button.njk +141 -0
  68. package/docs/src/components/card.njk +156 -0
  69. package/docs/src/components/carousel.njk +102 -0
  70. package/docs/src/components/chart.njk +814 -0
  71. package/docs/src/components/checkbox.njk +101 -0
  72. package/docs/src/components/combobox.njk +293 -0
  73. package/docs/src/components/command.njk +288 -0
  74. package/docs/src/components/dialog.njk +177 -0
  75. package/docs/src/components/dropdown-menu.njk +403 -0
  76. package/docs/src/components/empty.njk +157 -0
  77. package/docs/src/components/field.njk +459 -0
  78. package/docs/src/components/form.njk +79 -0
  79. package/docs/src/components/input-group.njk +372 -0
  80. package/docs/src/components/input.njk +90 -0
  81. package/docs/src/components/item.njk +320 -0
  82. package/docs/src/components/kbd.njk +76 -0
  83. package/docs/src/components/label.njk +41 -0
  84. package/docs/src/components/pagination.njk +48 -0
  85. package/docs/src/components/popover.njk +174 -0
  86. package/docs/src/components/progress.njk +44 -0
  87. package/docs/src/components/radio-group.njk +48 -0
  88. package/docs/src/components/select.njk +457 -0
  89. package/docs/src/components/sidebar.njk +219 -0
  90. package/docs/src/components/skeleton.njk +51 -0
  91. package/docs/src/components/slider.njk +47 -0
  92. package/docs/src/components/spinner.njk +214 -0
  93. package/docs/src/components/switch.njk +54 -0
  94. package/docs/src/components/table.njk +87 -0
  95. package/docs/src/components/tabs.njk +232 -0
  96. package/docs/src/components/textarea.njk +90 -0
  97. package/docs/src/components/theme-switcher.njk +111 -0
  98. package/docs/src/components/toast.njk +279 -0
  99. package/docs/src/components/tooltip.njk +53 -0
  100. package/docs/src/fragments/toast/error.njk +6 -0
  101. package/docs/src/fragments/toast/info.njk +5 -0
  102. package/docs/src/fragments/toast/success.njk +7 -0
  103. package/docs/src/fragments/toast/warning.njk +5 -0
  104. package/docs/src/index.njk +336 -0
  105. package/docs/src/installation.njk +275 -0
  106. package/docs/src/introduction.njk +63 -0
  107. package/docs/src/kitchen-sink.njk +52 -0
  108. package/docs/src/llms.txt +506 -0
  109. package/docs/src/robots.njk +7 -0
  110. package/docs/src/sitemap.njk +15 -0
  111. package/docs/src/test.njk +39 -0
  112. package/package.json +51 -0
  113. package/packages/cli/README.md +55 -0
  114. package/packages/cli/index.js +193 -0
  115. package/packages/cli/package.json +44 -0
  116. package/packages/css/README.md +63 -0
  117. package/packages/css/package.json +63 -0
  118. package/scripts/build.js +170 -0
  119. package/src/css/basecoat.cdn.css +2 -0
  120. package/src/css/basecoat.css +1310 -0
  121. package/src/jinja/command.html.jinja +206 -0
  122. package/src/jinja/dialog.html.jinja +94 -0
  123. package/src/jinja/dropdown-menu.html.jinja +124 -0
  124. package/src/jinja/popover.html.jinja +48 -0
  125. package/src/jinja/select.html.jinja +196 -0
  126. package/src/jinja/sidebar.html.jinja +144 -0
  127. package/src/jinja/tabs.html.jinja +78 -0
  128. package/src/jinja/toast.html.jinja +117 -0
  129. package/src/js/basecoat.js +99 -0
  130. package/src/js/command.js +175 -0
  131. package/src/js/dropdown-menu.js +171 -0
  132. package/src/js/popover.js +73 -0
  133. package/src/js/select.js +432 -0
  134. package/src/js/sidebar.js +104 -0
  135. package/src/js/tabs.js +63 -0
  136. package/src/js/toast.js +181 -0
  137. package/src/nunjucks/command.njk +206 -0
  138. package/src/nunjucks/dialog.njk +92 -0
  139. package/src/nunjucks/dropdown-menu.njk +124 -0
  140. package/src/nunjucks/popover.njk +48 -0
  141. package/src/nunjucks/select.njk +196 -0
  142. package/src/nunjucks/sidebar.njk +144 -0
  143. package/src/nunjucks/tabs.njk +78 -0
  144. package/src/nunjucks/toast.njk +117 -0
  145. package/wrangler.jsonc +7 -0
@@ -0,0 +1,219 @@
1
+ ---
2
+ layout: layouts/page.njk
3
+ title: Sidebar
4
+ description: A composable, themeable and customizable sidebar component.
5
+ toc:
6
+ - label: Usage
7
+ id: usage
8
+ children:
9
+ - label: HTML + JavaScript
10
+ id: usage-html-js
11
+ children:
12
+ - label: "Step 1: Include the JavaScript files"
13
+ id: usage-html-js-1
14
+ - label: "Step 2: Add your sidebar HTML"
15
+ id: usage-html-js-2
16
+ - label: HTML structure
17
+ id: usage-html-js-3
18
+ - label: JavaScript events
19
+ id: usage-html-js-4
20
+ - label: Jinja and Nunjucks
21
+ id: usage-macro
22
+ ---
23
+
24
+ {% from "macros/code_block.njk" import code_block %}
25
+ {% from "sidebar.njk" import sidebar %}
26
+
27
+ <h2 id="usage"><a href="#usage">Usage</a></h2>
28
+
29
+ <h3 id="usage-html-js"><a href="#usage-html-js">HTML + JavaScript</a></h3>
30
+
31
+ <h4 id="usage-html-js-1"><a href="#usage-html-js-1">Step 1: Include the JavaScript files</a></h4>
32
+
33
+ <section class="prose">
34
+ <p>You can either <a href="/installation/#install-cdn-all">include the JavaScript file for all the components</a>, or just the one for this component by adding this to the <code>&lt;head&gt;</code> of your page:</p>
35
+ </section>
36
+
37
+ {% set code_script %}<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/basecoat.min.js" defer></script>
38
+ <script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/sidebar.min.js" defer></script>{% endset %}
39
+ {{ code_block(code_script | prettyHtml, "html") }}
40
+
41
+ <div class="flex flex-wrap gap-2 my-6">
42
+ <a class="badge-outline" href="/installation/#install-js">
43
+ Components with JavaScript
44
+ {% lucide "arrow-right" %}
45
+ </a>
46
+ <a class="badge-outline" href="/installation/#install-cli">
47
+ Use the CLI
48
+ {% lucide "arrow-right" %}
49
+ </a>
50
+ <a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/js/sidebar.js" target="_blank">
51
+ sidebar.js
52
+ {% lucide "arrow-right" %}
53
+ </a>
54
+ </div>
55
+
56
+ <h4 id="usage-html-js-2"><a href="#usage-html-js-2">Step 2: Add your sidebar HTML</a></h4>
57
+
58
+ {% set code_html %}
59
+ {% set icon_square_terminal %}{% lucide "square-terminal" %}{% endset %}
60
+ {% set icon_bot %}{% lucide "bot" %}{% endset %}
61
+ {% set icon_settings %}{% lucide "settings" %}{% endset %}
62
+ {% set menu = [
63
+ { type: "group", label: "Getting started", items: [
64
+ { label: "Playground", url: "#", icon: icon_square_terminal },
65
+ { label: "Models", url: "#", icon: icon_bot },
66
+ { label: "Settings", type: "submenu", icon: icon_settings, items: [
67
+ { label: "General", url: "#" },
68
+ { label: "Team", url: "#" },
69
+ { label: "Billing", url: "#" },
70
+ { label: "Limits", url: "#" }
71
+ ] }
72
+ ]}
73
+ ] %}
74
+ {{ sidebar(
75
+ label="Sidebar navigation",
76
+ content_attrs={
77
+ "class": "scrollbar"
78
+ },
79
+ menu=menu
80
+ ) }}
81
+ <main>
82
+ <button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar'))">Toggle sidebar</button>
83
+ <h1>Content</h1>
84
+ </main>
85
+ {% endset %}
86
+ {{ code_block(code_html | prettyHtml, "html") }}
87
+
88
+ <h4 id="usage-html-js-3"><a href="#usage-html-js-3">HTML structure</a></h4>
89
+
90
+ <section class="prose">
91
+ <dl>
92
+ <dt><code class="highlight language-html">&lt;aside class="sidebar" aria-hidden="false"&gt;</code></dt>
93
+ <dd>Wraps around the entire component. It can have the following attributes:
94
+ <ul>
95
+ <li><code>aria-hidden="true"</code>: controls the default state of the sidebar (hidden or visible).</li>
96
+ <li><code>data-side="left"</code>: specifies the side of the sidebar (<code>left</code> or <code>right</code>, defaults to <code>left</code>).</li>
97
+ </ul>
98
+ </dd>
99
+ <dl>
100
+ <dt><code class="highlight language-html">&lt;nav&gt;</code></dt>
101
+ <dd>
102
+ <p>The navigation element that contains the sidebar's content. It can have the following attributes:</p>
103
+ <ul>
104
+ <li><code>id="{BUTTON_ID}"</code>: linked to by the <code>aria-labelledby</code> attribute of the listbox.</li>
105
+ <li><code>aria-haspopup="menu"</code>: indicates that the button opens a menu.</li>
106
+ <li><code>aria-controls="{ MENU_ID }"</code>: points to the menu's id.</li>
107
+ <li><code>aria-expanded="false"</code>: tracks the popover's state.</li>
108
+ </ul>
109
+ <dl>
110
+ <dt><code class="highlight language-html">&lt;header&gt;</code> <span class="badge-secondary">Optional</span></dt>
111
+ <dd>The header of the sidebar.</dd>
112
+ <dt><code class="highlight language-html">&lt;section&gt;</code></dt>
113
+ <dd>The main navigation list.
114
+ <dl>
115
+ <dt><code class="highlight language-html">&lt;div role="group"&gt;</code></dt>
116
+ <dd>Group of options, can have a <code>aria-labelledby</code> attribute to link to a heading.</dd>
117
+ <dt><code class="highlight language-html">&lt;span role="heading"&gt;</code></dt>
118
+ <dd>Group heading, must have an <code>id</code> attribute if you use the <code>aria-labelledby</code> attribute on the group.</dd>
119
+ <dt><code class="highlight language-html">&lt;ul&gt;</code></dt>
120
+ <dd>List of links or buttons.
121
+ <dl>
122
+ <dt><code class="highlight language-html">&lt;li&gt;</code></dt>
123
+ <dd>Individual item.
124
+ <dl>
125
+ <dt><code class="highlight language-html">&lt;a&gt;</code></dt>
126
+ <dd>A link. By default, clicking on a link will close the sidebar on mobile unless the <code>data-keep-mobile-sidebar-open</code> attribute is present.</dd>
127
+ <dt><code class="highlight language-html">&lt;button&gt;</code></dt>
128
+ <dd>A button. By default, clicking on a button will close the sidebar on mobile unless the <code>data-keep-mobile-sidebar-open</code> attribute is present.</dd>
129
+ <dt><code class="highlight language-html">&lt;details&gt;</code></dt>
130
+ <dd>Collapsible section.
131
+ <dl>
132
+ <dt><code class="highlight language-html">&lt;summary&gt;</code></dt>
133
+ <dd>Summary of the collapsible section.</dd>
134
+ <dt><code class="highlight language-html">&lt;ul&gt;</code></dt>
135
+ <dd>List of links or buttons.</dd>
136
+ </dl>
137
+ </dd>
138
+ </dl>
139
+ </dd>
140
+ </dl>
141
+ </dd>
142
+ </dl>
143
+ </dd>
144
+ <dt><code class="highlight language-html">&lt;footer&gt;</code> <span class="badge-secondary">Optional</span></dt>
145
+ <dd>The footer of the sidebar.</dd>
146
+ </dl>
147
+ </dd>
148
+ </dl>
149
+ </dd>
150
+ <dt><code class="highlight language-html">&lt;main&gt;</code></dt>
151
+ <dd>A wrapper for the content of the page.</dd>
152
+ <dt><code class="highlight language-html">&lt;button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar'))"&gt;</code></dt>
153
+ <dd>A button to toggle the sidebar. If you want to use multiple sidebars you will need to add unique ids to the sidebars (i.e. <code>&lt;aside class="sidebar" id="{SIDEBAR_ID}"&gt;</code>) and refer to them in the event <code>detail</code> (i.e. <code>document.dispatchEvent(new CustomEvent('basecoat:sidebar', { detail: { id: '{SIDEBAR_ID}' } }));</code>).</dd>
154
+ </dl>
155
+ </section>
156
+
157
+ <h4 id="usage-html-js-4"><a href="#usage-html-js-4">JavaScript events</a></h4>
158
+
159
+ <section class="prose">
160
+ <dl>
161
+ <dt><code>basecoat:initialized</code></dt>
162
+ <dd>Once the component is fully initialized, it dispatches a custom (non-bubbling) <code>basecoat:initialized</code> event on itself.</dd>
163
+ <dt><code>basecoat:sidebar</code></dt>
164
+ <dd>
165
+ <p>Sidebars listen for this event on <code>document</code> to open, close or toggle themselves. By default, the event will toggle the sidebar, but can be used to open or close if you add an <code>action</code> to the detail. Additionally, if you have multiple sidebars on the page, you can target a specific sidebar by adding its <code>id</code> to the detail:</p>
166
+ {% set code_trigger %}<!-- Toggles the sidebar -->
167
+ <button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar'));">Toggle sidebar</button>
168
+ <!-- Opens the `#main-navigation` sidebar -->
169
+ <button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar', { detail: { id: 'main-navigation', action: 'open' } }));">Open sidebar</button>
170
+ <!-- Closes the sidebar -->
171
+ <button type="button" onclick="document.dispatchEvent(new CustomEvent('basecoat:sidebar', { detail: { action: 'close' } }));">Close sidebar</button>{% endset %}
172
+ {{ code_block(code_trigger | prettyHtml, "html") }}
173
+ </dd>
174
+ </dl>
175
+ </section>
176
+
177
+ <h3 id="usage-macro"><a href="#usage-macro">Jinja and Nunjucks</a></h3>
178
+
179
+ <div class="prose">
180
+ <p>You can use the <code class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs">sidebar()</code> Nunjucks or Jinja macro for this component.</p>
181
+ </div>
182
+
183
+ <div class="flex flex-wrap gap-2 my-6">
184
+ <a class="badge-outline" href="/installation/#install-macros" target="_blank">
185
+ Use Nunjucks or Jinja macros
186
+ {% lucide "arrow-right" %}
187
+ </a>
188
+ <a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/jinja/sidebar.html.jinja" target="_blank">
189
+ Jinja macro
190
+ {% lucide "arrow-right" %}
191
+ </a>
192
+ <a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/sidebar.njk" target="_blank">
193
+ Nunjucks macro
194
+ {% lucide "arrow-right" %}
195
+ </a>
196
+ </div>
197
+
198
+ {% set raw_code %}{% raw %}{% set menu = [
199
+ { type: "group", label: "Getting started", items: [
200
+ { label: "Playground", url: "#" },
201
+ { label: "Models", url: "#" },
202
+ { label: "Settings", type: "submenu", items: [
203
+ { label: "General", url: "#" },
204
+ { label: "Team", url: "#" },
205
+ { label: "Billing", url: "#" },
206
+ { label: "Limits", url: "#" }
207
+ ] }
208
+ ]}
209
+ ] %}
210
+
211
+ {{ sidebar(
212
+ label="Sidebar navigation",
213
+ menu=menu
214
+ ) }}
215
+ <main>
216
+ <h1>Content</h1>
217
+ </main>
218
+ {% endraw %}{% endset%}
219
+ {{ code_block(raw_code, "jinja") }}
@@ -0,0 +1,51 @@
1
+ ---
2
+ layout: layouts/page.njk
3
+ title: Skeleton
4
+ description: Used to show a placeholder while content is loading.
5
+ toc:
6
+ - label: Usage
7
+ id: usage
8
+ - label: Examples
9
+ id: examples
10
+ children:
11
+ - label: Card
12
+ id: example-card
13
+ ---
14
+
15
+ {% from "macros/code_preview.njk" import code_preview %}
16
+ {% from "macros/code_block.njk" import code_block %}
17
+
18
+ <div class="alert mb-6">
19
+ {% lucide "circle-alert" %}
20
+ <h2>There is no dedicated skeleton component in Basecoat.</h2>
21
+ </div>
22
+
23
+ {% set code %}<div class="flex items-center gap-4">
24
+ <div class="bg-accent animate-pulse size-10 shrink-0 rounded-full"></div>
25
+ <div class="grid gap-2">
26
+ <div class="bg-accent animate-pulse rounded-md h-4 w-[150px]"></div>
27
+ <div class="bg-accent animate-pulse rounded-md h-4 w-[100px]"></div>
28
+ </div>
29
+ </div>{% endset %}
30
+ {{ code_preview("skeleton", code | prettyHtml) }}
31
+
32
+ <h2 id="usage"><a href="#usage">Usage</a></h2>
33
+
34
+ <p class="prose">Simply use the <code>animate-pulse</code> class to create a skeleton.</p>
35
+
36
+ {{ code_block(code) }}
37
+
38
+ <h2 id="examples"><a href="#examples">Examples</a></h2>
39
+
40
+ <h3 id="example-card"><a href="#example-card">Card</a></h3>
41
+
42
+ {% set code %}<div class="card w-full @md:w-auto @md:min-w-sm">
43
+ <header>
44
+ <div class="bg-accent animate-pulse rounded-md h-4 w-2/3"></div>
45
+ <div class="bg-accent animate-pulse rounded-md h-4 w-1/2"></div>
46
+ </header>
47
+ <section>
48
+ <div class="bg-accent animate-pulse rounded-md aspect-square w-full"></div>
49
+ </section>
50
+ </div>{% endset %}
51
+ {{ code_preview("skeleton-card", code | prettyHtml, "w-full max-w-md") }}
@@ -0,0 +1,47 @@
1
+ ---
2
+ layout: layouts/page.njk
3
+ title: Slider
4
+ description: An input where the user selects a value from within a given range.
5
+ toc:
6
+ - label: Usage
7
+ id: usage
8
+ ---
9
+
10
+ {% from "macros/code_preview.njk" import code_preview %}
11
+ {% from "macros/code_block.njk" import code_block %}
12
+
13
+ {% set code %}<input
14
+ type="range"
15
+ class="input w-full"
16
+ min="0"
17
+ max="100"
18
+ value="50"
19
+ >
20
+ <script>
21
+ (() => {
22
+ const sliders = document.querySelectorAll('input[type="range"].input');
23
+ if (!sliders) return;
24
+
25
+ const updateSlider = (el) => {
26
+ const min = parseFloat(el.min || 0);
27
+ const max = parseFloat(el.max || 100);
28
+ const value = parseFloat(el.value);
29
+ const percent = (max === min) ? 0 : ((value - min) / (max - min)) * 100;
30
+ el.style.setProperty('--slider-value', `${percent}%`);
31
+ };
32
+
33
+ sliders.forEach(slider => {
34
+ updateSlider(slider);
35
+ slider.addEventListener('input', (event) => updateSlider(event.target));
36
+ });
37
+ })();
38
+ </script>{% endset %}
39
+ {{ code_preview("slider", code, "w-full max-w-sm") }}
40
+
41
+ <h2 id="usage"><a href="#usage">Usage</a></h2>
42
+
43
+ <section class="prose">
44
+ <p>You can style an <code>&lt;input type="range"&gt;</code> element by either adding the <code>input</code> class or having a parent with the <code>form</code> class (<a href="/components/form">read more about form</a>). However, the range input also requries some Javacript code to be able to paint the left side of the slider.</p>
45
+ </section>
46
+
47
+ {{ code_block(code) }}
@@ -0,0 +1,214 @@
1
+ ---
2
+ layout: layouts/page.njk
3
+ title: Spinner
4
+ description: An indicator that can be used to show a loading state.
5
+ toc:
6
+ - label: Usage
7
+ id: usage
8
+ - label: Examples
9
+ id: examples
10
+ children:
11
+ - label: Size
12
+ id: example-size
13
+ - label: Color
14
+ id: example-color
15
+ - label: Button
16
+ id: example-button
17
+ - label: Badge
18
+ id: example-badge
19
+ - label: Input Group
20
+ id: example-input-group
21
+ - label: Empty
22
+ id: example-empty
23
+ - label: Item
24
+ id: example-item
25
+ ---
26
+
27
+ {% from "macros/code_preview.njk" import code_preview %}
28
+ {% from "macros/code_block.njk" import code_block %}
29
+
30
+ <div class="alert mb-6">
31
+ {% lucide "circle-alert" %}
32
+ <h2>There is no dedicated Spinner component in Basecoat.</h2>
33
+ </div>
34
+
35
+ {% set code_default %}
36
+ <article class="group/item flex items-center border text-sm rounded-md transition-colors flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-transparent bg-muted/50 p-4 gap-4">
37
+ <div class="flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:size-4">
38
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin" } %}
39
+ </div>
40
+ <div class="flex flex-1 flex-col gap-1">
41
+ <h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium line-clamp-1">Processing payment...</h3>
42
+ </div>
43
+ <div class="flex flex-col gap-1 flex-none text-center">
44
+ <p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">$100.00</p>
45
+ </div>
46
+ </article>
47
+ {% endset %}
48
+
49
+ {{ code_preview("spinner-default", code_default, class="w-full max-w-xs") }}
50
+
51
+ <h2 id="usage"><a href="#usage">Usage</a></h2>
52
+
53
+ <section class="prose">
54
+ <p>Spinners are pure HTML using the <code>loader-circle</code> Lucide icon with the <code>animate-spin</code> Tailwind utility. Add <code>role="status"</code> and <code>aria-label="Loading"</code> for accessibility.</p>
55
+ <p>Browse the examples below and copy the pattern that fits your needs.</p>
56
+ </section>
57
+
58
+ <h2 id="examples"><a href="#examples">Examples</a></h2>
59
+
60
+ <h3 id="example-size"><a href="#example-size">Size</a></h3>
61
+
62
+ <section class="prose">
63
+ <p>Use the <code>size-*</code> utility class to change the size of the spinner.</p>
64
+ </section>
65
+
66
+ {% set code_size %}
67
+ <div class="flex items-center gap-6">
68
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "size-3 animate-spin" } %}
69
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "size-4 animate-spin" } %}
70
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "size-6 animate-spin" } %}
71
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "size-8 animate-spin" } %}
72
+ </div>
73
+ {% endset %}
74
+
75
+ {{ code_preview("spinner-size", code_size) }}
76
+
77
+ <h3 id="example-color"><a href="#example-color">Color</a></h3>
78
+
79
+ <section class="prose">
80
+ <p>Use the <code>text-*</code> utility class to change the color of the spinner.</p>
81
+ </section>
82
+
83
+ {% set code_color %}
84
+ <div class="flex items-center gap-6">
85
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "size-6 animate-spin text-red-500" } %}
86
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "size-6 animate-spin text-green-500" } %}
87
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "size-6 animate-spin text-blue-500" } %}
88
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "size-6 animate-spin text-yellow-500" } %}
89
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "size-6 animate-spin text-purple-500" } %}
90
+ </div>
91
+ {% endset %}
92
+
93
+ {{ code_preview("spinner-color", code_color) }}
94
+
95
+ <h3 id="example-button"><a href="#example-button">Button</a></h3>
96
+
97
+ <section class="prose">
98
+ <p>Add a spinner to a button to indicate a loading state.</p>
99
+ </section>
100
+
101
+ {% set code_button %}
102
+ <div class="flex flex-col items-center gap-4">
103
+ <button class="btn-sm" disabled>
104
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin" } %}
105
+ Loading...
106
+ </button>
107
+ <button class="btn-sm-outline" disabled>
108
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin" } %}
109
+ Please wait
110
+ </button>
111
+ <button class="btn-sm-secondary" disabled>
112
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin" } %}
113
+ Processing
114
+ </button>
115
+ </div>
116
+ {% endset %}
117
+
118
+ {{ code_preview("spinner-button", code_button) }}
119
+
120
+ <h3 id="example-badge"><a href="#example-badge">Badge</a></h3>
121
+
122
+ {% set code_badge %}
123
+ <div class="flex items-center gap-4">
124
+ <span class="badge">
125
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin" } %}
126
+ Syncing
127
+ </span>
128
+ <span class="badge-secondary">
129
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin" } %}
130
+ Updating
131
+ </span>
132
+ <span class="badge-outline">
133
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin" } %}
134
+ Processing
135
+ </span>
136
+ </div>
137
+ {% endset %}
138
+
139
+ {{ code_preview("spinner-badge", code_badge) }}
140
+
141
+ <h3 id="example-input-group"><a href="#example-input-group">Input Group</a></h3>
142
+
143
+ <section class="prose">
144
+ <p>Input groups can have spinners positioned inside them.</p>
145
+ </section>
146
+
147
+ {% set code_input_group %}
148
+ <div class="flex w-full max-w-md flex-col gap-4">
149
+ <div class="relative">
150
+ <input type="text" class="input pr-9" placeholder="Send a message..." disabled>
151
+ <div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground [&>svg]:size-4">
152
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin" } %}
153
+ </div>
154
+ </div>
155
+
156
+ <div class="relative">
157
+ <textarea class="textarea pr-10 min-h-27 pb-12" placeholder="Send a message..." disabled></textarea>
158
+ <footer role="group" class="absolute bottom-0 px-3 pb-3 pt-1.5 flex items-center w-full gap-2">
159
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "size-4 animate-spin text-muted-foreground" } %}
160
+ <span class="text-sm text-muted-foreground">Validating...</span>
161
+ <button type="button" class="btn-sm-icon h-6 w-7.5 ml-auto rounded">
162
+ {% lucide "arrow-up" %}
163
+ </button>
164
+ </footer>
165
+ </div>
166
+ </div>
167
+ {% endset %}
168
+
169
+ {{ code_preview("spinner-input-group", code_input_group, class="w-full max-w-md") }}
170
+
171
+ <h3 id="example-empty"><a href="#example-empty">Empty</a></h3>
172
+
173
+ <section class="prose">
174
+ <p>Use a spinner in empty states to indicate a loading or processing state.</p>
175
+ </section>
176
+
177
+ {% set code_empty %}
178
+ <div class="flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg p-6 text-center text-balance md:p-12 text-neutral-800 dark:text-neutral-300">
179
+ <header class="flex max-w-sm flex-col items-center gap-3 text-center">
180
+ <div class="mb-2 bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-6">
181
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin size-4" } %}
182
+ </div>
183
+ <h3 class="text-lg font-semibold tracking-tight">Processing your request</h3>
184
+ <p class="text-muted-foreground text-sm/relaxed">Please wait while we process your request. Do not refresh the page.</p>
185
+ </header>
186
+ <section class="flex w-full max-w-sm min-w-0 flex-col items-center gap-3">
187
+ <button class="btn-sm-outline">Cancel</button>
188
+ </section>
189
+ </div>
190
+ {% endset %}
191
+
192
+ {{ code_preview("spinner-empty", code_empty) }}
193
+
194
+ <h3 id="example-item"><a href="#example-item">Item</a></h3>
195
+
196
+ {% set code_item %}
197
+ <article class="group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] border-border p-4 gap-4">
198
+ <div class="flex shrink-0 items-center justify-center gap-2 self-start [&_svg]:pointer-events-none size-8 border rounded-md bg-muted [&_svg:not([class*='size-'])]:size-4">
199
+ {% lucide "loader-circle", { "role": "status", "aria-label": "Loading", "class": "animate-spin size-4 text-muted-foreground" } %}
200
+ </div>
201
+ <div class="flex flex-1 flex-col gap-1">
202
+ <h3 class="flex w-fit items-center gap-2 text-sm leading-snug font-medium">Downloading...</h3>
203
+ <p class="text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4">129 MB / 1000 MB</p>
204
+ </div>
205
+ <button class="btn-sm-outline self-start">Cancel</button>
206
+ <footer class="flex basis-full items-center justify-between gap-2">
207
+ <div class="bg-primary/20 relative h-2 w-full overflow-hidden rounded-full">
208
+ <div id="demo-progress" class="bg-primary h-full w-full flex-1 transition-all" style="width: 13%"></div>
209
+ </div>
210
+ </footer>
211
+ </article>
212
+ {% endset %}
213
+
214
+ {{ code_preview("spinner-item", code_item, class="w-full max-w-md") }}
@@ -0,0 +1,54 @@
1
+ ---
2
+ layout: layouts/page.njk
3
+ title: Switch
4
+ description: A control that allows the user to toggle between checked and not checked.
5
+ toc:
6
+ - label: Usage
7
+ id: usage
8
+ - label: Examples
9
+ id: examples
10
+ children:
11
+ - label: Form
12
+ id: example-form
13
+ ---
14
+
15
+ {% from "macros/code_preview.njk" import code_preview %}
16
+ {% from "macros/code_block.njk" import code_block %}
17
+
18
+ {% set code %}<label class="label">
19
+ <input type="checkbox" name="switch" role="switch" class="input">
20
+ Airplane Mode
21
+ </label>{% endset %}
22
+ {{ code_preview("switch", code) }}
23
+
24
+ <h2 id="usage"><a href="#usage">Usage</a></h2>
25
+
26
+ <section class="prose">
27
+ <p>You can style a <code>&lt;input type="checkbox"&gt;</code> element as a switch by adding a <code>role="switch"</code> attribute, and adding the <code>input</code> class or having a parent with the <code>form</code> class (<a href="/components/form">read more about form</a>).</p>
28
+ </section>
29
+
30
+ {% set code %}<input type="checkbox" role="switch" class="input">{% endset %}
31
+ {{ code_block(code) }}
32
+
33
+ <h2 id="examples"><a href="#examples">Examples</a></h2>
34
+
35
+ <h3 id="example-form"><a href="#example-form">Form</a></h3>
36
+
37
+ {% set code %}<form class="form grid gap-4">
38
+ <h3 class="text-lg font-medium">Email Notifications</h3>
39
+ <div class="gap-2 flex flex-row items-start justify-between rounded-lg border p-4 shadow-xs">
40
+ <div class="flex flex-col gap-0.5">
41
+ <label for="demo-form-switch" class="leading-normal">Marketing emails</label>
42
+ <p class="text-muted-foreground text-sm">Receive emails about new products, features, and more.</p>
43
+ </div>
44
+ <input type="checkbox" id="demo-form-switch" role="switch">
45
+ </div>
46
+ <div class="gap-2 flex flex-row items-start justify-between rounded-lg border p-4 shadow-xs">
47
+ <div class="flex flex-col gap-0.5 opacity-60">
48
+ <label for="demo-form-switch-disabled" class="leading-normal">Marketing emails</label>
49
+ <p class="text-muted-foreground text-sm">Receive emails about new products, features, and more.</p>
50
+ </div>
51
+ <input type="checkbox" id="demo-form-switch-disabled" role="switch" disabled>
52
+ </div>
53
+ </form>{% endset %}
54
+ {{ code_preview("switch-form", code, "w-full") }}