farvist 0.5.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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +324 -0
  3. package/assets/farvist.js +187 -0
  4. package/assets/icons/farvist-icons.svg +49 -0
  5. package/assets/logo.svg +11 -0
  6. package/assets/og-image.svg +34 -0
  7. package/assets/patterns/blob.svg +9 -0
  8. package/assets/patterns/dots.svg +3 -0
  9. package/assets/patterns/grid.svg +3 -0
  10. package/assets/patterns/mesh.svg +20 -0
  11. package/dist/farvist.css +8967 -0
  12. package/dist/farvist.min.css +1 -0
  13. package/package.json +53 -0
  14. package/scss/abstracts/_functions.scss +85 -0
  15. package/scss/abstracts/_index.scss +9 -0
  16. package/scss/abstracts/_mixins.scss +78 -0
  17. package/scss/abstracts/_variables.scss +252 -0
  18. package/scss/base/_reset.scss +120 -0
  19. package/scss/base/_root.scss +87 -0
  20. package/scss/base/_typography.scss +102 -0
  21. package/scss/components/_accordion.scss +64 -0
  22. package/scss/components/_alerts.scss +63 -0
  23. package/scss/components/_avatar.scss +76 -0
  24. package/scss/components/_badges.scss +54 -0
  25. package/scss/components/_breadcrumb.scss +44 -0
  26. package/scss/components/_buttons.scss +163 -0
  27. package/scss/components/_callout.scss +37 -0
  28. package/scss/components/_cards.scss +74 -0
  29. package/scss/components/_chip.scss +61 -0
  30. package/scss/components/_dropdown.scss +90 -0
  31. package/scss/components/_empty-state.scss +37 -0
  32. package/scss/components/_forms.scss +125 -0
  33. package/scss/components/_icon.scss +25 -0
  34. package/scss/components/_list-group.scss +60 -0
  35. package/scss/components/_modal.scss +95 -0
  36. package/scss/components/_navbar.scss +67 -0
  37. package/scss/components/_pagination.scss +51 -0
  38. package/scss/components/_progress.scss +60 -0
  39. package/scss/components/_range.scss +58 -0
  40. package/scss/components/_rating.scss +26 -0
  41. package/scss/components/_segmented.scss +51 -0
  42. package/scss/components/_skeleton.scss +52 -0
  43. package/scss/components/_spinner.scss +60 -0
  44. package/scss/components/_stat.scss +32 -0
  45. package/scss/components/_stepper.scss +78 -0
  46. package/scss/components/_switch.scss +65 -0
  47. package/scss/components/_table.scss +51 -0
  48. package/scss/components/_tabs.scss +64 -0
  49. package/scss/components/_timeline.scss +77 -0
  50. package/scss/components/_toast.scss +79 -0
  51. package/scss/components/_tooltip.scss +45 -0
  52. package/scss/farvist.scss +60 -0
  53. package/scss/layout/_grid.scss +83 -0
  54. package/scss/utilities/_backgrounds.scss +110 -0
  55. package/scss/utilities/_effects.scss +118 -0
  56. package/scss/utilities/_spacing.scss +76 -0
  57. package/scss/utilities/_utilities.scss +213 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Farvist
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,324 @@
1
+ # Farvist <span>ยท glass edition</span>
2
+
3
+ [![npm](https://img.shields.io/npm/v/farvist?color=6d4af5)](https://www.npmjs.com/package/farvist)
4
+ [![minzipped size](https://img.shields.io/bundlephobia/minzip/farvist?color=22d3ee)](https://bundlephobia.com/package/farvist)
5
+ [![license](https://img.shields.io/badge/license-MIT-e879f9)](LICENSE)
6
+ ![dependencies](https://img.shields.io/badge/dependencies-0-34d399)
7
+
8
+ A lightweight, **Sass-powered CSS framework** with a futuristic, frosted-glass aesthetic โ€” a customizable alternative to Bootstrap and Tailwind. Every value is a design token in a Sass map, and the utility classes are **generated automatically** with `@for` and `@each` loops.
9
+
10
+ - ๐ŸชŸ **Glassmorphism** โ€” frosted `backdrop-filter` surfaces, translucent fills, inner highlights and deep shadows on every component.
11
+ - ๐ŸŒˆ **Neon & gradients** โ€” gradient buttons, gradient text, and colored glow utilities generated from token maps.
12
+ - ๐ŸŒŒ **Background library** โ€” mesh gradients, patterns, spotlights and fade-masks in one class (`.bg-mesh-aurora`, `.bg-grid`, `.bg-spotlight`, `.bg-fade`). A free feature Tailwind and Bootstrap make you hand-roll.
13
+ - ๐ŸŒ— **Dark & light themes** โ€” dark "space" theme by default; flip `data-theme="light"` on `<html>` to retheme at runtime via `--fv-*` custom properties.
14
+ - โš™๏ธ **Auto-generated utilities** โ€” spacing, display, flex, color, glow, gradient, sizing and more, produced by loops instead of hand-writing hundreds of rules.
15
+ - ๐Ÿงฉ **30+ glass components** โ€” buttons, badges, alerts, cards, forms, navbar, plus modal, dropdown, tabs, accordion, tooltip, toast, progress, spinner, skeleton, switch, avatar, breadcrumb, pagination, table and chips.
16
+ - ๐ŸŽจ **Assets included** โ€” a 35-icon SVG set (`currentColor`-driven), decorative SVGs (mesh, blob, grid, dots, logo) and a 2.5 KB optional JS companion for the interactive bits.
17
+ - ๐Ÿ–ผ๏ธ **Premade templates** โ€” a glass dashboard and a copy-paste block gallery in `examples/`.
18
+ - ๐Ÿ“ **12-column flexbox grid** with responsive columns and offsets.
19
+ - ๐Ÿชถ **~19 KB gzipped** (127 KB minified) โ€” and shrinks further when you trim the token maps.
20
+ - ๐Ÿฆพ **Modern & accessible** โ€” `@use`/`@forward` modules (no deprecated `@import`), `:focus-visible` rings, `prefers-reduced-motion` support, and a `@supports` fallback for browsers without `backdrop-filter`.
21
+
22
+ ---
23
+
24
+ ## Quick start
25
+
26
+ ### Use the compiled CSS
27
+
28
+ Via CDN (no build step โ€” once published to npm it's on jsDelivr automatically):
29
+
30
+ ```html
31
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/farvist/dist/farvist.min.css" />
32
+ <!-- optional 2.5 KB companion for modals, tabs, toasts, theme toggle -->
33
+ <script src="https://cdn.jsdelivr.net/npm/farvist/assets/farvist.js" defer></script>
34
+ ```
35
+
36
+ Or use the local build:
37
+
38
+ ```html
39
+ <link rel="stylesheet" href="dist/farvist.css" />
40
+ ```
41
+
42
+ The framework defaults to the **dark glass theme**. Open `index.html` for a full live demo (with a dark/light toggle). For best results give the page a rich background โ€” Farvist paints radial "orbs" behind the content so the glass has something to blur.
43
+
44
+ ### Build from source
45
+
46
+ You need [Node.js](https://nodejs.org). Dart Sass is pulled in as a dev dependency.
47
+
48
+ ```bash
49
+ npm install # install Dart Sass
50
+ npm run dev # build dist/farvist.css (expanded) + dist/farvist.min.css (minified)
51
+ npm run watch # rebuild on save
52
+ ```
53
+
54
+ | Script | What it does |
55
+ | ------------------- | --------------------------------------- |
56
+ | `npm run build` | Compile expanded `dist/farvist.css` |
57
+ | `npm run build:min` | Compile minified `dist/farvist.min.css` |
58
+ | `npm run dev` | Both of the above |
59
+ | `npm run watch` | Recompile on every change |
60
+
61
+ ---
62
+
63
+ ## Theming
64
+
65
+ ### Dark / light at runtime
66
+
67
+ The whole system is driven by `--fv-*` CSS custom properties. The default `:root` is the dark theme; a `[data-theme="light"]` block overrides the surface variables. Toggle it with one line of JS:
68
+
69
+ ```js
70
+ document.documentElement.setAttribute('data-theme', 'light'); // or remove it for dark
71
+ ```
72
+
73
+ Components reference the variables (`--fv-body-bg`, `--fv-glass-bg`, `--fv-glass-border`, `--fv-muted`, โ€ฆ) so they retheme instantly โ€” no recompile.
74
+
75
+ ### Re-theme at build time
76
+
77
+ Because everything is a token, you can recolor the whole framework by configuring the module:
78
+
79
+ ```scss
80
+ // my-theme.scss
81
+ @use 'farvist/scss/farvist' with (
82
+ $primary: #00e0ff, // your neon
83
+ $accent: #ff4dd2,
84
+ $glass-blur: 22px,
85
+ $border-radius: 1rem
86
+ );
87
+ ```
88
+
89
+ Or edit the maps directly in [`scss/abstracts/_variables.scss`](scss/abstracts/_variables.scss):
90
+
91
+ ```scss
92
+ $theme-colors: (primary, secondary, success, danger, warning, info, accent, light, dark);
93
+ $spacers: (0 โ€ฆ 8); // drives .m-*/.p-*
94
+ $font-sizes: (xs โ€ฆ 5xl); // drives .fs-*
95
+ $gradients: (primary, accent, sunset, aurora); // drives .bg-gradient-*, .btn-gradient-*, .text-gradient-*
96
+ $glows: (primary, accent, info, success, danger, warning); // drives .glow-*
97
+ $grid-breakpoints: (xs:0, sm:576, md:768, lg:992, xl:1200, xxl:1400);
98
+ ```
99
+
100
+ Add a key to `$theme-colors` and you instantly get `.btn-*`, `.badge-*`, `.alert-*`, `.text-*`, `.bg-*`, `.border-*` for it โ€” that's the payoff of generating from maps.
101
+
102
+ **Want it smaller?** Remove a breakpoint from `$grid-breakpoints` or a step from `$spacers` and recompile โ€” the generators emit fewer classes instantly.
103
+
104
+ ---
105
+
106
+ ## How the generators work
107
+
108
+ This is what the project is really about. Two Sass loops do the heavy lifting.
109
+
110
+ ### `@for` โ€” numeric scales
111
+
112
+ The 12-column grid and the opacity scale are produced by counting:
113
+
114
+ ```scss
115
+ @for $i from 1 through $grid-columns {
116
+ .col-#{$i} {
117
+ flex: 0 0 auto;
118
+ width: math.percentage(math.div($i, $grid-columns));
119
+ }
120
+ }
121
+ // โ†’ .col-1 { width: 8.333% } โ€ฆ .col-12 { width: 100% }
122
+ ```
123
+
124
+ ### `@each` โ€” walking the token maps
125
+
126
+ Spacing utilities come from **nested** `@each` loops over breakpoints ร— properties ร— scale ร— sides:
127
+
128
+ ```scss
129
+ @each $bp in map.keys($grid-breakpoints) {
130
+ $infix: breakpoint-infix($bp); // '' , '-sm', '-md', โ€ฆ
131
+ @include media-up($bp) {
132
+ @each $prop, $abbrev in (margin: 'm', padding: 'p') {
133
+ @each $size, $length in $spacers {
134
+ @each $side, $targets in $spacing-sides {
135
+ .#{$abbrev}#{$side}#{$infix}-#{$size} { /* โ€ฆ */ }
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ // โ†’ .m-0, .mt-3, .px-4, .py-lg-0, .me-2, โ€ฆ (the full responsive matrix)
142
+ ```
143
+
144
+ The glass look itself is a mixin reading runtime variables, so dark/light just works:
145
+
146
+ ```scss
147
+ @mixin glass($strong: false) {
148
+ background-color: var(--fv-glass-bg);
149
+ backdrop-filter: blur(var(--fv-glass-blur)) saturate(var(--fv-glass-saturate));
150
+ border: 1px solid var(--fv-glass-border);
151
+ box-shadow: var(--fv-shadow-lg), inset 0 1px 0 rgba(255,255,255,.1);
152
+ }
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Class reference (cheatsheet)
158
+
159
+ ### Glass & effects
160
+
161
+ | Class | Description |
162
+ | --- | --- |
163
+ | `.glass`, `.glass-strong` | Frosted surface (light/strong) |
164
+ | `.glow`, `.glow-{primary\|accent\|info\|success\|danger\|warning}` | Neon glow shadow |
165
+ | `.text-glow` | Neon halo on text |
166
+ | `.bg-gradient-{primary\|accent\|sunset\|aurora}` | Gradient fill |
167
+ | `.text-gradient`, `.text-gradient-{name}` | Gradient-clipped text |
168
+ | `.bg-animated` | Slowly drifting gradient (pair with `.bg-gradient-*`) |
169
+ | `.hover-lift`, `.animate-float` | Motion helpers |
170
+
171
+ ### Backgrounds
172
+
173
+ | Class | Description |
174
+ | --- | --- |
175
+ | `.bg-mesh-{aurora\|sunset\|ocean\|nebula}` | Multi-orb mesh gradient backdrop |
176
+ | `.bg-mesh` | Default mesh (aurora) |
177
+ | `.bg-grid`, `.bg-graph`, `.bg-dots`, `.bg-lines`, `.bg-noise` | Theme-aware patterns |
178
+ | `.bg-pattern-sm`, `.bg-pattern-lg` | Pattern tile-size modifiers |
179
+ | `.bg-spotlight`, `.bg-spotlight-{color}` | Radial glow backdrop |
180
+ | `.bg-fade`, `.bg-fade-b`, `.bg-fade-x` | Fade a background to the edges. Uses `mask-image`, which fades children too โ€” put it on a dedicated decorative layer, and best with the transparent patterns (`.bg-grid.bg-fade`) |
181
+ | `.bg-drift` | Slowly pan a mesh/gradient (reduced-motion safe) |
182
+
183
+ Mesh stops come from the `$bg-meshes` map and reference your theme colors, so they restyle with the palette.
184
+
185
+ ### Components
186
+
187
+ **Buttons** โ€” `.btn` + `.btn-{color}` / `.btn-outline-{color}` / `.btn-gradient-{name}` / `.btn-glass` / `.btn-link` / `.btn-sm` / `.btn-lg` / `.btn-block`
188
+
189
+ **Badges** โ€” `.badge` + `.badge-{color}` / `.badge-soft-{color}` / `.badge-pill`
190
+
191
+ **Alerts** โ€” `.alert-{color}` with `.alert-heading` / `.alert-link`
192
+
193
+ **Cards** โ€” `.card` (`.card-glow`, `.card-solid`, `.card-header/-body/-footer`, `.card-title/-subtitle/-text`, `.card-img-top`)
194
+
195
+ **Forms** โ€” `.form-label` `.form-control` `.form-select` `.form-text` ยท `.form-check` (`.form-check-input` `.form-check-label`) ยท `.input-group` (`.input-group-text`) ยท validation `.is-valid`/`.is-invalid` + `.valid-feedback`/`.invalid-feedback`
196
+
197
+ **Nav** โ€” `.navbar` `.navbar-brand` `.navbar-nav` `.nav-link`
198
+
199
+ **Modal** (native `<dialog>` + JS) โ€” `.modal` (`.modal-header/-title/-body/-footer/-close`)
200
+
201
+ **Dropdown** (native `<details>`) โ€” `.dropdown` `.dropdown-menu` (`.dropdown-menu-end`) `.dropdown-item` `.dropdown-divider`
202
+
203
+ **Tabs** (JS) โ€” `.tabs` `.tab` `.tab-panel` (toggle with `data-fv-tab="#panel"`)
204
+
205
+ **Accordion** (native `<details>`, no JS) โ€” `.accordion-item` `.accordion-body`
206
+
207
+ **Tooltip** (CSS-only) โ€” `data-tooltip="text"` on any element
208
+
209
+ **Toast** (JS) โ€” `.toast` `.toast-{color}` (`.toast-title/-message/-close`), spawn via `Farvist.toast()`
210
+
211
+ **Table** โ€” `.table` `.table-hover` `.table-striped` `.table-responsive`
212
+
213
+ **Progress** โ€” `.progress` `.progress-bar` `.progress-{color}` `.progress-striped` `.progress-animated`
214
+
215
+ **Spinner** โ€” `.spinner` `.spinner-{sm|lg|color}` ยท `.spinner-dots`
216
+
217
+ **Skeleton** โ€” `.skeleton` `.skeleton-{text|title|avatar|btn}`
218
+
219
+ **Switch** โ€” `.switch` `.switch-track` `.switch-thumb`
220
+
221
+ **Avatar** โ€” `.avatar` `.avatar-{sm|lg|xl|color}` ยท `.avatar-status[-busy|-away]` ยท `.avatar-group`
222
+
223
+ **Chip** โ€” `.chip` `.chip-{color}` `.chip-close`
224
+
225
+ **Breadcrumb / Pagination** โ€” `.breadcrumb` `.breadcrumb-item` ยท `.pagination` `.page-item` `.page-link`
226
+
227
+ **Stepper** โ€” `.stepper` `.step` (`.is-active` / `.is-done`) `.step-dot` `.step-label`
228
+
229
+ **Timeline** โ€” `.timeline` `.timeline-item` (`.timeline-{color}`) `.timeline-marker` `.timeline-content`
230
+
231
+ **Segmented** (radio, no JS) โ€” `.segmented` + `input`/`label` pairs ยท **Rating** โ€” `.rating` (+ `.rating-empty`)
232
+
233
+ **Stat** โ€” `.stat-value` `.stat-label` `.stat-trend` (`-up`/`-down`) ยท **Range** โ€” `.form-range`
234
+
235
+ **Empty state** โ€” `.empty-state` `.empty-state-icon/-title/-text` ยท **List group** โ€” `.list-group` `.list-group-item`
236
+
237
+ **Callout** โ€” `.callout` `.callout-{color}` `.callout-title`
238
+
239
+ Theme colors: `primary ยท secondary ยท success ยท danger ยท warning ยท info ยท accent ยท light ยท dark`.
240
+
241
+ ### Icons, assets & JavaScript
242
+
243
+ A 35-icon SVG sprite ships in `assets/icons/`. Icons inherit `currentColor`, so they take the text color (and any `.text-glow`):
244
+
245
+ ```html
246
+ <svg class="icon"><use href="assets/icons/farvist-icons.svg#i-bell"/></svg>
247
+ <svg class="icon icon-lg text-accent"><use href="assets/icons/farvist-icons.svg#i-star"/></svg>
248
+ ```
249
+
250
+ Sizes: `.icon` (1em) ยท `.icon-sm` ยท `.icon-lg` ยท `.icon-xl`. Decorative SVGs (`logo.svg`, `patterns/mesh.svg`, `blob.svg`, `grid.svg`, `dots.svg`) live in `assets/`, and theme-aware CSS patterns are built in: `.bg-grid` `.bg-dots` `.bg-noise`.
251
+
252
+ The optional **2.5 KB** companion powers the interactive components โ€” drop it in with `defer`:
253
+
254
+ ```html
255
+ <script src="assets/farvist.js" defer></script>
256
+ ```
257
+
258
+ It wires modals (`data-fv-open="#id"` / `data-fv-dismiss`), tabs (`data-fv-tab`), theme toggling (`data-fv-theme-toggle`), dropdown outside-click close, and toasts:
259
+
260
+ ```js
261
+ Farvist.toast({ title: 'Saved', message: 'All good.', variant: 'success', timeout: 4000 });
262
+ ```
263
+
264
+ Everything else (accordion, dropdown, tooltip, switch, progressโ€ฆ) is pure CSS and needs no JS.
265
+
266
+ ### Layout
267
+
268
+ `.container` / `.container-fluid` ยท `.row` ยท `.col`, `.col-auto`, `.col-{1โ€“12}`, `.col-{smโ€ฆxxl}-{1โ€“12}` ยท `.offset-{0โ€“11}` ยท `.g-* .gx-* .gy-*`
269
+
270
+ ### Spacing โ€” `{property}{sides}{-breakpoint}-{size}`
271
+
272
+ `m` = margin, `p` = padding ยท sides `t e b s x y` (or blank for all) ยท size `0โ€“8` ยท optional `-smโ€ฆ-xxl` ยท negatives `m-n3` ยท centering `mx-auto`
273
+
274
+ ```
275
+ .mt-3 .px-4 .p-0 .m-md-5 .py-lg-0 .mx-auto .mb-n2
276
+ ```
277
+
278
+ ### Display, flex, type, color, borders, sizing, position
279
+
280
+ `.d-{none|block|flex|grid|โ€ฆ}` (responsive) ยท `.flex-{row|column|wrap}` `.justify-content-*` `.align-items-*` `.gap-*` ยท `.h1`โ€“`.h6` (heading scale on any element) `.fs-{xsโ€ฆ5xl}` `.fw-{lightโ€ฆblack}` `.text-{start|center|end}` `.text-truncate` ยท `.text-{color|muted|body}` `.bg-{color|surface}` `.border-{color}` ยท `.border` `.rounded-{smโ€ฆ2xl|pill|circle}` `.shadow-{smโ€ฆxl}` ยท `.w-{25โ€ฆ100}` `.vh-100` ยท `.position-*` `.sticky-top` `.fixed-top` `.z-*` `.opacity-{0โ€ฆ100}`
281
+
282
+ ---
283
+
284
+ ## Project structure
285
+
286
+ ```
287
+ scss/
288
+ โ”œโ”€โ”€ farvist.scss # entry point โ€” @use's every layer in cascade order
289
+ โ”œโ”€โ”€ abstracts/ # no CSS output โ€” tokens + tools
290
+ โ”‚ โ”œโ”€โ”€ _variables.scss # design tokens incl. glass/glow/gradient maps
291
+ โ”‚ โ”œโ”€โ”€ _functions.scss # color(), spacer(), breakpoint helpers, YIQ contrast
292
+ โ”‚ โ”œโ”€โ”€ _mixins.scss # media-up(), glass(), button-variant(), visually-hidden()
293
+ โ”‚ โ””โ”€โ”€ _index.scss # @forward barrel โ†’ @use '../abstracts' as *
294
+ โ”œโ”€โ”€ base/ # reset, :root vars + dark/light themes, typography
295
+ โ”œโ”€โ”€ layout/ # container + 12-col flexbox grid
296
+ โ”œโ”€โ”€ components/ # 20 partials: buttons โ€ฆ modal, dropdown, tabs, toast, โ€ฆ
297
+ โ””โ”€โ”€ utilities/ # generated helper classes
298
+ โ”œโ”€โ”€ _spacing.scss # the nested-@each spacing matrix
299
+ โ”œโ”€โ”€ _utilities.scss # display, flex, text, color, border, sizing, โ€ฆ
300
+ โ”œโ”€โ”€ _effects.scss # glass, glow, gradient, motion + @supports
301
+ โ””โ”€โ”€ _backgrounds.scss # mesh gradients, patterns, spotlights, fade-masks
302
+ assets/
303
+ โ”œโ”€โ”€ farvist.js # 2.5 KB optional companion (modal, tabs, toast, theme)
304
+ โ”œโ”€โ”€ logo.svg # wordmark + gradient mark
305
+ โ”œโ”€โ”€ icons/farvist-icons.svg # 35-icon sprite (currentColor)
306
+ โ””โ”€โ”€ patterns/ # mesh, blob, grid, dots decorative SVGs
307
+ examples/
308
+ โ”œโ”€โ”€ dashboard.html # full glass dashboard template
309
+ โ””โ”€โ”€ blocks.html # copy-paste sections (hero, pricing, auth, โ€ฆ)
310
+ dist/ # compiled output (committed for convenience)
311
+ index.html # live demo with a dark/light toggle
312
+ ```
313
+
314
+ The architecture follows the **7-1 pattern** and Sass's modern module system: each partial pulls in what it needs with `@use '../abstracts' as *;`.
315
+
316
+ ---
317
+
318
+ ## Browser support
319
+
320
+ Modern evergreen browsers (Chrome, Edge, Firefox, Safari). The glass effect uses `backdrop-filter`; browsers without it fall back to opaque surfaces via `@supports`. Also relies on CSS custom properties, flexbox, logical properties (`margin-inline`) and `:focus-visible`.
321
+
322
+ ## License
323
+
324
+ MIT โ€” do whatever you like.
@@ -0,0 +1,187 @@
1
+ /*!
2
+ * Farvistrap โ€” tiny vanilla-JS companion (~2.5 KB) for the interactive bits.
3
+ * No dependencies. Drop it in with: <script src="assets/farvistrap.js" defer></script>
4
+ *
5
+ * Modals : <button data-fv-open="#id"> + <dialog class="modal" id="id">
6
+ * close with [data-fv-dismiss], a backdrop click, or Esc (native)
7
+ * Tabs : <button class="tab" data-fv-tab="#panel"> + <div class="tab-panel" id="panel">
8
+ * ARIA roles, roving tabindex and arrow-key nav are applied for you
9
+ * Theme : <button data-fv-theme-toggle> (flips data-theme on <html>)
10
+ * Toasts : Farvistrap.toast({ title, message, variant, timeout })
11
+ * Dropdowns close on an outside click or Esc.
12
+ *
13
+ * On load it also adds role="progressbar" + aria-value* to .progress bars and
14
+ * full tablist semantics to .tabs, so the components are accessible by default.
15
+ */
16
+ (function () {
17
+ 'use strict';
18
+
19
+ var qsa = function (sel, root) {
20
+ return Array.prototype.slice.call((root || document).querySelectorAll(sel));
21
+ };
22
+
23
+ // Activate a tab and its panel โ€” scoped to the tab's OWN .tabs group, so
24
+ // sibling tab groups under one container don't clobber each other.
25
+ function activateTab(tab) {
26
+ var list = tab.closest('.tabs');
27
+ if (!list) return;
28
+ qsa('.tab', list).forEach(function (t) {
29
+ var on = t === tab;
30
+ t.classList.toggle('active', on);
31
+ t.setAttribute('aria-selected', on ? 'true' : 'false');
32
+ t.setAttribute('tabindex', on ? '0' : '-1');
33
+ var sel = t.getAttribute('data-fv-tab');
34
+ var panel = sel && document.querySelector(sel);
35
+ if (panel) panel.classList.toggle('active', on);
36
+ });
37
+ }
38
+
39
+ // ---- Click delegation ----
40
+ document.addEventListener('click', function (e) {
41
+ var t = e.target;
42
+ if (!(t instanceof Element)) return;
43
+
44
+ // Close any open dropdown the click landed outside of (runs unconditionally,
45
+ // before the early-returning branches below).
46
+ qsa('details.dropdown[open]').forEach(function (dd) {
47
+ if (!dd.contains(t)) dd.removeAttribute('open');
48
+ });
49
+
50
+ var opener = t.closest('[data-fv-open]');
51
+ if (opener) {
52
+ var dlg = document.querySelector(opener.getAttribute('data-fv-open'));
53
+ if (dlg && dlg.showModal) dlg.showModal();
54
+ return;
55
+ }
56
+
57
+ var dismiss = t.closest('[data-fv-dismiss]');
58
+ if (dismiss) {
59
+ var d = dismiss.closest('dialog');
60
+ if (d) d.close();
61
+ return;
62
+ }
63
+
64
+ if (t.matches('dialog.modal')) {
65
+ var r = t.getBoundingClientRect();
66
+ if (e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom) t.close();
67
+ }
68
+
69
+ var tab = t.closest('[data-fv-tab]');
70
+ if (tab) activateTab(tab);
71
+
72
+ if (t.closest('[data-fv-theme-toggle]')) {
73
+ var root = document.documentElement;
74
+ if (root.getAttribute('data-theme') === 'light') root.removeAttribute('data-theme');
75
+ else root.setAttribute('data-theme', 'light');
76
+ }
77
+ });
78
+
79
+ // ---- Keyboard: Esc closes dropdowns; arrows move between tabs ----
80
+ document.addEventListener('keydown', function (e) {
81
+ if (e.key === 'Escape') {
82
+ qsa('details.dropdown[open]').forEach(function (dd) {
83
+ dd.removeAttribute('open');
84
+ var s = dd.querySelector('summary');
85
+ if (s) s.focus();
86
+ });
87
+ }
88
+
89
+ var t = e.target;
90
+ if (t instanceof Element && t.matches('.tab') && t.closest('.tabs')) {
91
+ var tabs = qsa('.tab', t.closest('.tabs'));
92
+ var i = tabs.indexOf(t), n = tabs.length, j = -1;
93
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') j = (i + 1) % n;
94
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') j = (i - 1 + n) % n;
95
+ else if (e.key === 'Home') j = 0;
96
+ else if (e.key === 'End') j = n - 1;
97
+ if (j > -1) { e.preventDefault(); activateTab(tabs[j]); tabs[j].focus(); }
98
+ }
99
+ });
100
+
101
+ // ---- Progressive ARIA enhancement (tabs + progress) ----
102
+ function enhance() {
103
+ qsa('.tabs').forEach(function (list) {
104
+ list.setAttribute('role', 'tablist');
105
+ qsa('.tab', list).forEach(function (tab, i) {
106
+ var on = tab.classList.contains('active');
107
+ tab.setAttribute('role', 'tab');
108
+ tab.setAttribute('aria-selected', on ? 'true' : 'false');
109
+ tab.setAttribute('tabindex', on ? '0' : '-1');
110
+ var sel = tab.getAttribute('data-fv-tab');
111
+ var panel = sel && document.querySelector(sel);
112
+ if (panel) {
113
+ if (!tab.id) tab.id = (panel.id || 'fvtab' + i) + '-tab';
114
+ tab.setAttribute('aria-controls', panel.id);
115
+ panel.setAttribute('role', 'tabpanel');
116
+ if (!panel.hasAttribute('tabindex')) panel.setAttribute('tabindex', '0');
117
+ panel.setAttribute('aria-labelledby', tab.id);
118
+ }
119
+ });
120
+ });
121
+
122
+ qsa('.progress > .progress-bar').forEach(function (bar) {
123
+ bar.setAttribute('role', 'progressbar');
124
+ bar.setAttribute('aria-valuemin', '0');
125
+ bar.setAttribute('aria-valuemax', '100');
126
+ var w = bar.style.width;
127
+ if (w && w.indexOf('%') > -1) bar.setAttribute('aria-valuenow', String(parseFloat(w)));
128
+ });
129
+ }
130
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', enhance);
131
+ else enhance();
132
+
133
+ // ---- Toasts ----
134
+ function toast(opts) {
135
+ opts = opts || {};
136
+ var container = document.querySelector('.toast-container');
137
+ if (!container) {
138
+ container = document.createElement('div');
139
+ container.className = 'toast-container';
140
+ document.body.appendChild(container);
141
+ }
142
+
143
+ var el = document.createElement('div');
144
+ el.className = 'toast' + (opts.variant ? ' toast-' + opts.variant : '');
145
+ el.setAttribute('role', 'status');
146
+
147
+ var body = document.createElement('div');
148
+ body.className = 'toast-body';
149
+ if (opts.title) {
150
+ var ti = document.createElement('div');
151
+ ti.className = 'toast-title';
152
+ ti.textContent = opts.title;
153
+ body.appendChild(ti);
154
+ }
155
+ if (opts.message) {
156
+ var m = document.createElement('div');
157
+ m.className = 'toast-message';
158
+ m.textContent = opts.message;
159
+ body.appendChild(m);
160
+ }
161
+
162
+ var close = document.createElement('button');
163
+ close.className = 'toast-close';
164
+ close.setAttribute('aria-label', 'Close');
165
+ close.innerHTML = '&times;';
166
+
167
+ el.appendChild(body);
168
+ el.appendChild(close);
169
+ container.appendChild(el);
170
+
171
+ var timer, gone = false;
172
+ function remove() {
173
+ if (gone) return; // idempotent: manual close cancels auto-dismiss
174
+ gone = true;
175
+ if (timer) clearTimeout(timer);
176
+ el.classList.add('is-leaving');
177
+ setTimeout(function () { el.remove(); }, 250);
178
+ }
179
+ close.addEventListener('click', remove);
180
+
181
+ var timeout = opts.timeout == null ? 4000 : opts.timeout;
182
+ if (timeout) timer = setTimeout(remove, timeout);
183
+ return el;
184
+ }
185
+
186
+ window.Farvistrap = { toast: toast, enhance: enhance };
187
+ })();
@@ -0,0 +1,49 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Farvist icon set โ€” 35 line icons, 24ร—24, stroke=currentColor.
4
+ Usage: <svg class="icon"><use href="assets/icons/farvist-icons.svg#i-check"/></svg>
5
+ They inherit the current text color (and any text-glow) automatically.
6
+ -->
7
+ <svg xmlns="http://www.w3.org/2000/svg" style="display:none">
8
+ <defs>
9
+ <style>
10
+ symbol { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
11
+ </style>
12
+ </defs>
13
+
14
+ <symbol id="i-check" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></symbol>
15
+ <symbol id="i-x" viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></symbol>
16
+ <symbol id="i-plus" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></symbol>
17
+ <symbol id="i-minus" viewBox="0 0 24 24"><line x1="5" y1="12" x2="19" y2="12"/></symbol>
18
+ <symbol id="i-chevron-down" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"/></symbol>
19
+ <symbol id="i-chevron-right" viewBox="0 0 24 24"><polyline points="9 6 15 12 9 18"/></symbol>
20
+ <symbol id="i-arrow-right" viewBox="0 0 24 24"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></symbol>
21
+ <symbol id="i-arrow-left" viewBox="0 0 24 24"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></symbol>
22
+ <symbol id="i-menu" viewBox="0 0 24 24"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></symbol>
23
+ <symbol id="i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></symbol>
24
+ <symbol id="i-user" viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol>
25
+ <symbol id="i-users" viewBox="0 0 24 24"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></symbol>
26
+ <symbol id="i-bell" viewBox="0 0 24 24"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></symbol>
27
+ <symbol id="i-settings" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></symbol>
28
+ <symbol id="i-heart" viewBox="0 0 24 24"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></symbol>
29
+ <symbol id="i-star" viewBox="0 0 24 24"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></symbol>
30
+ <symbol id="i-home" viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></symbol>
31
+ <symbol id="i-mail" viewBox="0 0 24 24"><rect x="2" y="4" width="20" height="16" rx="2"/><polyline points="22 6 12 13 2 6"/></symbol>
32
+ <symbol id="i-lock" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></symbol>
33
+ <symbol id="i-eye" viewBox="0 0 24 24"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></symbol>
34
+ <symbol id="i-download" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></symbol>
35
+ <symbol id="i-upload" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></symbol>
36
+ <symbol id="i-trash" viewBox="0 0 24 24"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></symbol>
37
+ <symbol id="i-edit" viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></symbol>
38
+ <symbol id="i-calendar" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></symbol>
39
+ <symbol id="i-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></symbol>
40
+ <symbol id="i-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></symbol>
41
+ <symbol id="i-alert" viewBox="0 0 24 24"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></symbol>
42
+ <symbol id="i-external" viewBox="0 0 24 24"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></symbol>
43
+ <symbol id="i-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></symbol>
44
+ <symbol id="i-moon" viewBox="0 0 24 24"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></symbol>
45
+ <symbol id="i-zap" viewBox="0 0 24 24"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></symbol>
46
+ <symbol id="i-sparkles" viewBox="0 0 24 24"><path d="M12 3l1.6 4.6L18 9l-4.4 1.4L12 15l-1.6-4.6L6 9l4.4-1.4z"/><path d="M19 14l.7 2.1 2.1.7-2.1.7L19 20l-.7-2.1L16.2 17l2.1-.7z"/></symbol>
47
+ <symbol id="i-code" viewBox="0 0 24 24"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></symbol>
48
+ <symbol id="i-layers" viewBox="0 0 24 24"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></symbol>
49
+ </svg>
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 248 48" fill="none">
2
+ <defs>
3
+ <linearGradient id="fv-logo" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0" stop-color="#6d4af5"/>
5
+ <stop offset="0.5" stop-color="#e879f9"/>
6
+ <stop offset="1" stop-color="#22d3ee"/>
7
+ </linearGradient>
8
+ </defs>
9
+ <rect x="11" y="11" width="26" height="26" rx="8" transform="rotate(45 24 24)" fill="url(#fv-logo)"/>
10
+ <text x="54" y="32" font-family="Inter, 'Segoe UI', system-ui, sans-serif" font-size="23" font-weight="800" letter-spacing="-0.5" fill="#e6ebf5">Farvist</text>
11
+ </svg>
@@ -0,0 +1,34 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" font-family="Inter, 'Segoe UI', system-ui, sans-serif">
2
+ <defs>
3
+ <linearGradient id="og-text" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0" stop-color="#6d4af5"/>
5
+ <stop offset="0.5" stop-color="#e879f9"/>
6
+ <stop offset="1" stop-color="#22d3ee"/>
7
+ </linearGradient>
8
+ <radialGradient id="og-o1" cx="85%" cy="0%" r="55%">
9
+ <stop offset="0" stop-color="#6d4af5" stop-opacity="0.55"/><stop offset="1" stop-color="#6d4af5" stop-opacity="0"/>
10
+ </radialGradient>
11
+ <radialGradient id="og-o2" cx="5%" cy="100%" r="55%">
12
+ <stop offset="0" stop-color="#22d3ee" stop-opacity="0.4"/><stop offset="1" stop-color="#22d3ee" stop-opacity="0"/>
13
+ </radialGradient>
14
+ <radialGradient id="og-o3" cx="60%" cy="115%" r="45%">
15
+ <stop offset="0" stop-color="#e879f9" stop-opacity="0.35"/><stop offset="1" stop-color="#e879f9" stop-opacity="0"/>
16
+ </radialGradient>
17
+ </defs>
18
+
19
+ <rect width="1200" height="630" fill="#060912"/>
20
+ <rect width="1200" height="630" fill="url(#og-o1)"/>
21
+ <rect width="1200" height="630" fill="url(#og-o2)"/>
22
+ <rect width="1200" height="630" fill="url(#og-o3)"/>
23
+
24
+ <!-- glass panel accent -->
25
+ <rect x="760" y="150" width="330" height="330" rx="28" fill="#ffffff" fill-opacity="0.06" stroke="#ffffff" stroke-opacity="0.14"/>
26
+ <rect x="62" y="62" width="44" height="44" rx="13" transform="rotate(45 84 84)" fill="url(#og-text)"/>
27
+
28
+ <text x="84" y="300" font-size="92" font-weight="800" letter-spacing="-2" fill="url(#og-text)">Farvist</text>
29
+ <text x="86" y="362" font-size="36" font-weight="600" fill="#e6ebf5">The futuristic glass CSS framework</text>
30
+ <text x="86" y="412" font-size="26" font-weight="500" fill="#95a3c4">Frosted surfaces ยท neon glow ยท gradients ยท 17 KB gzipped</text>
31
+
32
+ <rect x="86" y="470" width="220" height="58" rx="14" fill="url(#og-text)"/>
33
+ <text x="196" y="507" font-size="24" font-weight="700" fill="#0b0f1d" text-anchor="middle">Get started โ€” free</text>
34
+ </svg>
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" fill="none">
2
+ <defs>
3
+ <linearGradient id="fv-blob" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0" stop-color="#6d4af5"/>
5
+ <stop offset="1" stop-color="#e879f9"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <path fill="url(#fv-blob)" d="M205 38c44 7 92 18 118 52s31 86 18 130-58 70-104 86-100 18-138-9-58-79-55-126 31-90 68-116 49-30 93-23z"/>
9
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <circle cx="2" cy="2" r="1.3" fill="#ffffff" fill-opacity="0.1"/>
3
+ </svg>