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.
- package/LICENSE +21 -0
- package/README.md +324 -0
- package/assets/farvist.js +187 -0
- package/assets/icons/farvist-icons.svg +49 -0
- package/assets/logo.svg +11 -0
- package/assets/og-image.svg +34 -0
- package/assets/patterns/blob.svg +9 -0
- package/assets/patterns/dots.svg +3 -0
- package/assets/patterns/grid.svg +3 -0
- package/assets/patterns/mesh.svg +20 -0
- package/dist/farvist.css +8967 -0
- package/dist/farvist.min.css +1 -0
- package/package.json +53 -0
- package/scss/abstracts/_functions.scss +85 -0
- package/scss/abstracts/_index.scss +9 -0
- package/scss/abstracts/_mixins.scss +78 -0
- package/scss/abstracts/_variables.scss +252 -0
- package/scss/base/_reset.scss +120 -0
- package/scss/base/_root.scss +87 -0
- package/scss/base/_typography.scss +102 -0
- package/scss/components/_accordion.scss +64 -0
- package/scss/components/_alerts.scss +63 -0
- package/scss/components/_avatar.scss +76 -0
- package/scss/components/_badges.scss +54 -0
- package/scss/components/_breadcrumb.scss +44 -0
- package/scss/components/_buttons.scss +163 -0
- package/scss/components/_callout.scss +37 -0
- package/scss/components/_cards.scss +74 -0
- package/scss/components/_chip.scss +61 -0
- package/scss/components/_dropdown.scss +90 -0
- package/scss/components/_empty-state.scss +37 -0
- package/scss/components/_forms.scss +125 -0
- package/scss/components/_icon.scss +25 -0
- package/scss/components/_list-group.scss +60 -0
- package/scss/components/_modal.scss +95 -0
- package/scss/components/_navbar.scss +67 -0
- package/scss/components/_pagination.scss +51 -0
- package/scss/components/_progress.scss +60 -0
- package/scss/components/_range.scss +58 -0
- package/scss/components/_rating.scss +26 -0
- package/scss/components/_segmented.scss +51 -0
- package/scss/components/_skeleton.scss +52 -0
- package/scss/components/_spinner.scss +60 -0
- package/scss/components/_stat.scss +32 -0
- package/scss/components/_stepper.scss +78 -0
- package/scss/components/_switch.scss +65 -0
- package/scss/components/_table.scss +51 -0
- package/scss/components/_tabs.scss +64 -0
- package/scss/components/_timeline.scss +77 -0
- package/scss/components/_toast.scss +79 -0
- package/scss/components/_tooltip.scss +45 -0
- package/scss/farvist.scss +60 -0
- package/scss/layout/_grid.scss +83 -0
- package/scss/utilities/_backgrounds.scss +110 -0
- package/scss/utilities/_effects.scss +118 -0
- package/scss/utilities/_spacing.scss +76 -0
- package/scss/utilities/_utilities.scss +213 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist · Components · Spinner
|
|
3
|
+
// A ring loader and a bouncing-dots loader, both color-themeable.
|
|
4
|
+
// =============================================================================
|
|
5
|
+
|
|
6
|
+
@use 'sass:map';
|
|
7
|
+
@use '../abstracts' as *;
|
|
8
|
+
|
|
9
|
+
.spinner {
|
|
10
|
+
display: inline-block;
|
|
11
|
+
width: 1.5rem;
|
|
12
|
+
height: 1.5rem;
|
|
13
|
+
vertical-align: middle;
|
|
14
|
+
border: 3px solid var(--fv-glass-border);
|
|
15
|
+
border-top-color: var(--fv-primary);
|
|
16
|
+
border-radius: 50%;
|
|
17
|
+
animation: fv-spin 0.7s linear infinite;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.spinner-sm { width: 1rem; height: 1rem; border-width: 2px; }
|
|
21
|
+
.spinner-lg { width: 2.5rem; height: 2.5rem; border-width: 4px; }
|
|
22
|
+
|
|
23
|
+
@each $name, $clr in $theme-colors {
|
|
24
|
+
.spinner-#{$name} { border-top-color: $clr; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Three bouncing dots.
|
|
28
|
+
.spinner-dots {
|
|
29
|
+
display: inline-flex;
|
|
30
|
+
gap: 0.3rem;
|
|
31
|
+
vertical-align: middle;
|
|
32
|
+
|
|
33
|
+
> span {
|
|
34
|
+
width: 0.5rem;
|
|
35
|
+
height: 0.5rem;
|
|
36
|
+
background-color: var(--fv-primary);
|
|
37
|
+
border-radius: 50%;
|
|
38
|
+
animation: fv-bounce 0.6s ease-in-out infinite;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
> span:nth-child(2) { animation-delay: 0.12s; }
|
|
42
|
+
> span:nth-child(3) { animation-delay: 0.24s; }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@keyframes fv-spin {
|
|
46
|
+
to { transform: rotate(360deg); }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@keyframes fv-bounce {
|
|
50
|
+
0%, 100% { transform: translateY(0); opacity: 0.5; }
|
|
51
|
+
50% { transform: translateY(-0.45rem); opacity: 1; }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Keep a slow, continuous spin so the loading indicator stays meaningful. The
|
|
55
|
+
// !important is required to beat the global reduced-motion reset (which would
|
|
56
|
+
// otherwise freeze the ring mid-rotation).
|
|
57
|
+
@media (prefers-reduced-motion: reduce) {
|
|
58
|
+
.spinner { animation: fv-spin 1.6s linear infinite !important; }
|
|
59
|
+
.spinner-dots > span { animation: none; }
|
|
60
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist · Components · Stat
|
|
3
|
+
// Typographic helpers for metric tiles (drop inside a .card).
|
|
4
|
+
// <div class="stat-value">8,420</div>
|
|
5
|
+
// <div class="stat-label">Active users</div>
|
|
6
|
+
// <span class="stat-trend stat-trend-up">+12%</span>
|
|
7
|
+
// =============================================================================
|
|
8
|
+
|
|
9
|
+
@use 'sass:map';
|
|
10
|
+
@use '../abstracts' as *;
|
|
11
|
+
|
|
12
|
+
.stat-value {
|
|
13
|
+
font-size: map.get($font-sizes, '3xl');
|
|
14
|
+
font-weight: map.get($font-weights, 'black');
|
|
15
|
+
line-height: 1.1;
|
|
16
|
+
letter-spacing: -0.02em;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.stat-label {
|
|
20
|
+
color: var(--fv-muted);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.stat-trend {
|
|
24
|
+
display: inline-flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: 0.25rem;
|
|
27
|
+
font-size: map.get($font-sizes, 'sm');
|
|
28
|
+
font-weight: map.get($font-weights, 'semibold');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.stat-trend-up { color: var(--fv-success); }
|
|
32
|
+
.stat-trend-down { color: var(--fv-danger); }
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist · Components · Stepper
|
|
3
|
+
// <ol class="stepper">
|
|
4
|
+
// <li class="step is-done"><span class="step-dot">1</span><span class="step-label">Account</span></li>
|
|
5
|
+
// <li class="step is-active"><span class="step-dot">2</span><span class="step-label">Plan</span></li>
|
|
6
|
+
// <li class="step"><span class="step-dot">3</span><span class="step-label">Done</span></li>
|
|
7
|
+
// </ol>
|
|
8
|
+
// =============================================================================
|
|
9
|
+
|
|
10
|
+
@use 'sass:map';
|
|
11
|
+
@use '../abstracts' as *;
|
|
12
|
+
|
|
13
|
+
.stepper {
|
|
14
|
+
display: flex;
|
|
15
|
+
gap: spacer(2);
|
|
16
|
+
padding: 0;
|
|
17
|
+
margin: 0 0 spacer(4);
|
|
18
|
+
list-style: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.step {
|
|
22
|
+
position: relative;
|
|
23
|
+
flex: 1 1 0;
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
align-items: center;
|
|
27
|
+
gap: spacer(2);
|
|
28
|
+
text-align: center;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.step-dot {
|
|
32
|
+
position: relative;
|
|
33
|
+
z-index: 1;
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
width: 2rem;
|
|
38
|
+
height: 2rem;
|
|
39
|
+
font-weight: map.get($font-weights, 'semibold');
|
|
40
|
+
color: var(--fv-muted);
|
|
41
|
+
background-color: var(--fv-body-bg);
|
|
42
|
+
border: $border-width solid var(--fv-glass-border);
|
|
43
|
+
border-radius: 50%;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.step-label {
|
|
47
|
+
font-size: map.get($font-sizes, 'sm');
|
|
48
|
+
color: var(--fv-muted);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Connector line from each step's center to the next.
|
|
52
|
+
.step:not(:last-child)::after {
|
|
53
|
+
content: '';
|
|
54
|
+
position: absolute;
|
|
55
|
+
top: 1rem;
|
|
56
|
+
left: 50%;
|
|
57
|
+
width: 100%;
|
|
58
|
+
height: 2px;
|
|
59
|
+
background-color: var(--fv-glass-border);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.step.is-active .step-dot {
|
|
63
|
+
color: #ffffff;
|
|
64
|
+
background-image: var(--fv-gradient-primary);
|
|
65
|
+
border-color: transparent;
|
|
66
|
+
box-shadow: var(--fv-glow-primary);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.step.is-done .step-dot {
|
|
70
|
+
color: #ffffff;
|
|
71
|
+
background-color: var(--fv-success);
|
|
72
|
+
border-color: transparent;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.step.is-active .step-label,
|
|
76
|
+
.step.is-done .step-label {
|
|
77
|
+
color: var(--fv-body-color);
|
|
78
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist · Components · Switch (toggle)
|
|
3
|
+
// Markup (give the input a label — visible text or aria-label):
|
|
4
|
+
// <label class="switch">
|
|
5
|
+
// <input type="checkbox" aria-label="Dark mode" />
|
|
6
|
+
// <span class="switch-track"></span>
|
|
7
|
+
// <span class="switch-thumb"></span>
|
|
8
|
+
// </label>
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
@use 'sass:map';
|
|
12
|
+
@use '../abstracts' as *;
|
|
13
|
+
|
|
14
|
+
.switch {
|
|
15
|
+
position: relative;
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
|
|
20
|
+
input {
|
|
21
|
+
position: absolute;
|
|
22
|
+
width: 0;
|
|
23
|
+
height: 0;
|
|
24
|
+
opacity: 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.switch-track {
|
|
29
|
+
width: 2.6rem;
|
|
30
|
+
height: 1.5rem;
|
|
31
|
+
background-color: var(--fv-glass-bg-strong);
|
|
32
|
+
border: $border-width solid var(--fv-glass-border);
|
|
33
|
+
border-radius: map.get($radii, 'pill');
|
|
34
|
+
transition: background-color $transition-base, border-color $transition-base;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.switch-thumb {
|
|
38
|
+
position: absolute;
|
|
39
|
+
top: 50%;
|
|
40
|
+
left: 0.22rem;
|
|
41
|
+
width: 1.04rem;
|
|
42
|
+
height: 1.04rem;
|
|
43
|
+
background-color: #ffffff;
|
|
44
|
+
border-radius: 50%;
|
|
45
|
+
box-shadow: var(--fv-shadow-sm);
|
|
46
|
+
transform: translateY(-50%);
|
|
47
|
+
transition: transform $transition-base;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.switch input:checked ~ .switch-track {
|
|
51
|
+
background-image: var(--fv-gradient-primary);
|
|
52
|
+
border-color: transparent;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.switch input:checked ~ .switch-thumb {
|
|
56
|
+
transform: translateY(-50%) translateX(1.08rem);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.switch input:focus-visible ~ .switch-track {
|
|
60
|
+
box-shadow: var(--fv-focus-ring);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.switch input:disabled ~ .switch-track {
|
|
64
|
+
opacity: 0.5;
|
|
65
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist · Components · Table
|
|
3
|
+
// Clean data table with hover/striped modifiers; drop inside a .card for glass.
|
|
4
|
+
// =============================================================================
|
|
5
|
+
|
|
6
|
+
@use 'sass:map';
|
|
7
|
+
@use '../abstracts' as *;
|
|
8
|
+
|
|
9
|
+
.table {
|
|
10
|
+
width: 100%;
|
|
11
|
+
border-collapse: collapse;
|
|
12
|
+
color: var(--fv-body-color);
|
|
13
|
+
|
|
14
|
+
th,
|
|
15
|
+
td {
|
|
16
|
+
padding: spacer(3) spacer(4);
|
|
17
|
+
text-align: left;
|
|
18
|
+
vertical-align: middle;
|
|
19
|
+
border-bottom: $border-width solid var(--fv-border-color);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
thead th {
|
|
23
|
+
font-size: map.get($font-sizes, 'sm');
|
|
24
|
+
font-weight: map.get($font-weights, 'semibold');
|
|
25
|
+
color: var(--fv-muted);
|
|
26
|
+
text-transform: uppercase;
|
|
27
|
+
letter-spacing: 0.04em;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
tbody tr {
|
|
31
|
+
transition: background-color $transition-base;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
tbody tr:last-child td {
|
|
35
|
+
border-bottom: 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.table-hover tbody tr:hover {
|
|
40
|
+
background-color: var(--fv-glass-bg);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.table-striped tbody tr:nth-child(odd) {
|
|
44
|
+
background-color: var(--fv-glass-bg);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Horizontal scroll wrapper for narrow viewports.
|
|
48
|
+
.table-responsive {
|
|
49
|
+
overflow-x: auto;
|
|
50
|
+
-webkit-overflow-scrolling: touch;
|
|
51
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist · Components · Tabs (segmented)
|
|
3
|
+
// JS-driven: .tab buttons carry data-fv-tab="#panelId"; farvist.js toggles
|
|
4
|
+
// the .active class on the clicked tab and its panel.
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
@use 'sass:map';
|
|
8
|
+
@use '../abstracts' as *;
|
|
9
|
+
|
|
10
|
+
.tabs {
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
flex-wrap: wrap;
|
|
13
|
+
gap: spacer(1);
|
|
14
|
+
padding: spacer(1);
|
|
15
|
+
margin-bottom: spacer(4);
|
|
16
|
+
background-color: var(--fv-glass-bg);
|
|
17
|
+
border: $border-width solid var(--fv-glass-border);
|
|
18
|
+
border-radius: map.get($radii, 'lg');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.tab {
|
|
22
|
+
padding: spacer(2) spacer(4);
|
|
23
|
+
font-size: map.get($font-sizes, 'base');
|
|
24
|
+
font-weight: map.get($font-weights, 'medium');
|
|
25
|
+
color: var(--fv-muted);
|
|
26
|
+
background: transparent;
|
|
27
|
+
border: 0;
|
|
28
|
+
border-radius: map.get($radii, 'md');
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
transition: color $transition-base, background-color $transition-base;
|
|
31
|
+
|
|
32
|
+
&:hover {
|
|
33
|
+
color: var(--fv-body-color);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&:focus-visible {
|
|
37
|
+
outline: 0;
|
|
38
|
+
box-shadow: var(--fv-focus-ring);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&.active {
|
|
42
|
+
color: var(--fv-body-color);
|
|
43
|
+
background-color: var(--fv-glass-bg-strong);
|
|
44
|
+
box-shadow: var(--fv-shadow-sm);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.tab-panel {
|
|
49
|
+
display: none;
|
|
50
|
+
|
|
51
|
+
&.active {
|
|
52
|
+
display: block;
|
|
53
|
+
animation: fv-fade-in 0.25s ease;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@keyframes fv-fade-in {
|
|
58
|
+
from { opacity: 0; transform: translateY(0.4rem); }
|
|
59
|
+
to { opacity: 1; transform: none; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@media (prefers-reduced-motion: reduce) {
|
|
63
|
+
.tab-panel.active { animation: none; }
|
|
64
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist · Components · Timeline
|
|
3
|
+
// <ul class="timeline">
|
|
4
|
+
// <li class="timeline-item timeline-success">
|
|
5
|
+
// <div class="timeline-marker"></div>
|
|
6
|
+
// <div class="timeline-content">
|
|
7
|
+
// <div class="timeline-title">Deployed</div>
|
|
8
|
+
// <div class="timeline-time">2 min ago</div>
|
|
9
|
+
// </div>
|
|
10
|
+
// </li>
|
|
11
|
+
// </ul>
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
@use 'sass:map';
|
|
15
|
+
@use '../abstracts' as *;
|
|
16
|
+
|
|
17
|
+
.timeline {
|
|
18
|
+
padding: 0;
|
|
19
|
+
margin: 0;
|
|
20
|
+
list-style: none;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.timeline-item {
|
|
24
|
+
display: flex;
|
|
25
|
+
gap: spacer(4);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.timeline-marker {
|
|
29
|
+
flex: 0 0 auto;
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
align-items: center;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.timeline-marker::before {
|
|
36
|
+
content: '';
|
|
37
|
+
flex: 0 0 auto;
|
|
38
|
+
width: 0.9rem;
|
|
39
|
+
height: 0.9rem;
|
|
40
|
+
margin-top: 0.3rem;
|
|
41
|
+
background-color: var(--fv-primary);
|
|
42
|
+
border: 2px solid var(--fv-body-bg);
|
|
43
|
+
border-radius: 50%;
|
|
44
|
+
box-shadow: 0 0 0 1px var(--fv-glass-border);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.timeline-marker::after {
|
|
48
|
+
content: '';
|
|
49
|
+
flex: 1 1 auto;
|
|
50
|
+
width: 2px;
|
|
51
|
+
margin-top: 0.25rem;
|
|
52
|
+
background-color: var(--fv-glass-border);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.timeline-item:last-child .timeline-marker::after {
|
|
56
|
+
display: none;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.timeline-content {
|
|
60
|
+
padding-bottom: spacer(5);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.timeline-title {
|
|
64
|
+
font-weight: map.get($font-weights, 'semibold');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.timeline-time {
|
|
68
|
+
font-size: map.get($font-sizes, 'sm');
|
|
69
|
+
color: var(--fv-muted);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Per-item dot colors.
|
|
73
|
+
@each $name, $clr in $theme-colors {
|
|
74
|
+
.timeline-#{$name} .timeline-marker::before {
|
|
75
|
+
background-color: $clr;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist · Components · Toast
|
|
3
|
+
// Spawn with farvist.js: Farvist.toast({ title, message, variant }).
|
|
4
|
+
// =============================================================================
|
|
5
|
+
|
|
6
|
+
@use 'sass:map';
|
|
7
|
+
@use '../abstracts' as *;
|
|
8
|
+
|
|
9
|
+
.toast-container {
|
|
10
|
+
position: fixed;
|
|
11
|
+
top: spacer(5);
|
|
12
|
+
right: spacer(5);
|
|
13
|
+
z-index: map.get($z-layers, 'tooltip');
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: spacer(2);
|
|
17
|
+
width: min(22rem, calc(100vw - #{spacer(6)}));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.toast {
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: flex-start;
|
|
23
|
+
gap: spacer(3);
|
|
24
|
+
padding: spacer(3) spacer(4);
|
|
25
|
+
border-radius: map.get($radii, 'lg');
|
|
26
|
+
border-left: 3px solid var(--fv-primary);
|
|
27
|
+
@include glass($strong: true);
|
|
28
|
+
box-shadow: var(--fv-shadow-xl);
|
|
29
|
+
animation: fv-toast-in 0.3s ease;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.toast-body {
|
|
33
|
+
flex: 1 1 auto;
|
|
34
|
+
min-width: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.toast-title {
|
|
38
|
+
font-weight: map.get($font-weights, 'semibold');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.toast-message {
|
|
42
|
+
font-size: map.get($font-sizes, 'sm');
|
|
43
|
+
color: var(--fv-muted);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.toast-close {
|
|
47
|
+
flex-shrink: 0;
|
|
48
|
+
padding: 0;
|
|
49
|
+
font-size: 1.2rem;
|
|
50
|
+
line-height: 1;
|
|
51
|
+
color: var(--fv-muted);
|
|
52
|
+
background: transparent;
|
|
53
|
+
border: 0;
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
|
|
56
|
+
&:hover { color: var(--fv-body-color); }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Colored left accent per variant.
|
|
60
|
+
@each $name, $clr in $theme-colors {
|
|
61
|
+
.toast-#{$name} { border-left-color: $clr; }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.toast.is-leaving {
|
|
65
|
+
animation: fv-toast-out 0.25s ease forwards;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@keyframes fv-toast-in {
|
|
69
|
+
from { opacity: 0; transform: translateX(1rem); }
|
|
70
|
+
to { opacity: 1; transform: none; }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@keyframes fv-toast-out {
|
|
74
|
+
to { opacity: 0; transform: translateX(1rem); }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@media (prefers-reduced-motion: reduce) {
|
|
78
|
+
.toast { animation: none; }
|
|
79
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist · Components · Tooltip (CSS-only)
|
|
3
|
+
// Add `data-tooltip="text"` to any element. Appears above on hover/focus.
|
|
4
|
+
//
|
|
5
|
+
// ACCESSIBILITY: the tooltip text is rendered with `content: attr()`, which is
|
|
6
|
+
// DECORATIVE — it is not exposed to assistive tech. The host must therefore be
|
|
7
|
+
// focusable AND carry its own accessible name. For an icon-only control, give it
|
|
8
|
+
// a real label, e.g. `<button aria-label="Notifications" data-tooltip="Notifications">`.
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
@use 'sass:map';
|
|
12
|
+
@use '../abstracts' as *;
|
|
13
|
+
|
|
14
|
+
[data-tooltip] {
|
|
15
|
+
position: relative;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
[data-tooltip]::after {
|
|
19
|
+
content: attr(data-tooltip);
|
|
20
|
+
position: absolute;
|
|
21
|
+
left: 50%;
|
|
22
|
+
bottom: calc(100% + 0.5rem);
|
|
23
|
+
z-index: map.get($z-layers, 'tooltip');
|
|
24
|
+
padding: spacer(1) spacer(3);
|
|
25
|
+
font-size: map.get($font-sizes, 'sm');
|
|
26
|
+
font-weight: map.get($font-weights, 'medium');
|
|
27
|
+
white-space: nowrap;
|
|
28
|
+
color: var(--fv-body-color);
|
|
29
|
+
background-color: var(--fv-glass-bg-strong);
|
|
30
|
+
backdrop-filter: blur(var(--fv-glass-blur)) saturate(var(--fv-glass-saturate));
|
|
31
|
+
-webkit-backdrop-filter: blur(var(--fv-glass-blur)) saturate(var(--fv-glass-saturate));
|
|
32
|
+
border: $border-width solid var(--fv-glass-border);
|
|
33
|
+
border-radius: map.get($radii, 'md');
|
|
34
|
+
box-shadow: var(--fv-shadow-lg);
|
|
35
|
+
opacity: 0;
|
|
36
|
+
pointer-events: none;
|
|
37
|
+
transform: translateX(-50%) translateY(0.25rem);
|
|
38
|
+
transition: opacity $transition-base, transform $transition-base;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[data-tooltip]:hover::after,
|
|
42
|
+
[data-tooltip]:focus-visible::after {
|
|
43
|
+
opacity: 1;
|
|
44
|
+
transform: translateX(-50%) translateY(0);
|
|
45
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist — a lightweight, Sass-powered CSS framework
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// Single entry point. Layers are loaded low-specificity first so that utilities
|
|
5
|
+
// (loaded last) can always win the cascade.
|
|
6
|
+
//
|
|
7
|
+
// Base → resets and element defaults
|
|
8
|
+
// Layout → container and grid system
|
|
9
|
+
// Components → buttons, cards, forms, …
|
|
10
|
+
// Utilities → generated single-purpose helpers
|
|
11
|
+
//
|
|
12
|
+
// Build: npm run dev (writes dist/farvist.css and dist/farvist.min.css)
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
// Base
|
|
16
|
+
@use 'base/root';
|
|
17
|
+
@use 'base/reset';
|
|
18
|
+
@use 'base/typography';
|
|
19
|
+
|
|
20
|
+
// Layout
|
|
21
|
+
@use 'layout/grid';
|
|
22
|
+
|
|
23
|
+
// Components
|
|
24
|
+
@use 'components/buttons';
|
|
25
|
+
@use 'components/badges';
|
|
26
|
+
@use 'components/alerts';
|
|
27
|
+
@use 'components/cards';
|
|
28
|
+
@use 'components/forms';
|
|
29
|
+
@use 'components/navbar';
|
|
30
|
+
@use 'components/icon';
|
|
31
|
+
@use 'components/table';
|
|
32
|
+
@use 'components/avatar';
|
|
33
|
+
@use 'components/chip';
|
|
34
|
+
@use 'components/progress';
|
|
35
|
+
@use 'components/spinner';
|
|
36
|
+
@use 'components/skeleton';
|
|
37
|
+
@use 'components/breadcrumb';
|
|
38
|
+
@use 'components/pagination';
|
|
39
|
+
@use 'components/tooltip';
|
|
40
|
+
@use 'components/switch';
|
|
41
|
+
@use 'components/tabs';
|
|
42
|
+
@use 'components/accordion';
|
|
43
|
+
@use 'components/dropdown';
|
|
44
|
+
@use 'components/modal';
|
|
45
|
+
@use 'components/toast';
|
|
46
|
+
@use 'components/stepper';
|
|
47
|
+
@use 'components/timeline';
|
|
48
|
+
@use 'components/segmented';
|
|
49
|
+
@use 'components/rating';
|
|
50
|
+
@use 'components/stat';
|
|
51
|
+
@use 'components/empty-state';
|
|
52
|
+
@use 'components/list-group';
|
|
53
|
+
@use 'components/callout';
|
|
54
|
+
@use 'components/range';
|
|
55
|
+
|
|
56
|
+
// Utilities (last — highest cascade priority)
|
|
57
|
+
@use 'utilities/spacing';
|
|
58
|
+
@use 'utilities/utilities';
|
|
59
|
+
@use 'utilities/effects';
|
|
60
|
+
@use 'utilities/backgrounds';
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Farvist · Layout · Container & flexbox grid
|
|
3
|
+
// A 12-column flexbox grid. The column and offset classes are generated with a
|
|
4
|
+
// nested `@for` (columns) inside an `@each` (breakpoints) loop.
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
@use 'sass:map';
|
|
8
|
+
@use 'sass:math';
|
|
9
|
+
@use '../abstracts' as *;
|
|
10
|
+
|
|
11
|
+
// -- Containers ---------------------------------------------------------------
|
|
12
|
+
.container,
|
|
13
|
+
.container-fluid {
|
|
14
|
+
width: 100%;
|
|
15
|
+
margin-inline: auto;
|
|
16
|
+
padding-inline: math.div($grid-gutter, 2);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// A fixed `.container` steps up to each breakpoint's max-width.
|
|
20
|
+
@each $name, $width in $container-max-widths {
|
|
21
|
+
@include media-up($name) {
|
|
22
|
+
.container {
|
|
23
|
+
max-width: $width;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// -- Row ----------------------------------------------------------------------
|
|
29
|
+
// Negative inline margin cancels the columns' gutter padding so edges align.
|
|
30
|
+
.row {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-wrap: wrap;
|
|
33
|
+
margin-inline: math.div(-$grid-gutter, 2);
|
|
34
|
+
|
|
35
|
+
> * {
|
|
36
|
+
width: 100%;
|
|
37
|
+
max-width: 100%;
|
|
38
|
+
padding-inline: math.div($grid-gutter, 2);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// -- Auto / equal-width columns ----------------------------------------------
|
|
43
|
+
.col {
|
|
44
|
+
flex: 1 0 0%;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.col-auto {
|
|
48
|
+
flex: 0 0 auto;
|
|
49
|
+
width: auto;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// -- Generated fixed-width columns & offsets ---------------------------------
|
|
53
|
+
@each $bp in map.keys($grid-breakpoints) {
|
|
54
|
+
$infix: breakpoint-infix($bp);
|
|
55
|
+
|
|
56
|
+
@include media-up($bp) {
|
|
57
|
+
// .col-6, .col-md-4, ... built with a @for loop over the column count.
|
|
58
|
+
@for $i from 1 through $grid-columns {
|
|
59
|
+
.col#{$infix}-#{$i} {
|
|
60
|
+
flex: 0 0 auto;
|
|
61
|
+
width: math.percentage(math.div($i, $grid-columns));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// .offset-3, .offset-lg-2, ... push a column to the right.
|
|
66
|
+
@for $i from 0 through $grid-columns - 1 {
|
|
67
|
+
$offset: 0;
|
|
68
|
+
@if $i != 0 {
|
|
69
|
+
$offset: math.percentage(math.div($i, $grid-columns));
|
|
70
|
+
}
|
|
71
|
+
.offset#{$infix}-#{$i} {
|
|
72
|
+
margin-left: $offset;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// -- Gutter utilities (gap-based, opt-in) ------------------------------------
|
|
79
|
+
@each $key, $value in $spacers {
|
|
80
|
+
.g-#{$key} { gap: $value; }
|
|
81
|
+
.gx-#{$key} { column-gap: $value; }
|
|
82
|
+
.gy-#{$key} { row-gap: $value; }
|
|
83
|
+
}
|