@wovenui/components 0.0.0-stage → 1.0.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 (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +284 -2
  3. package/dist/components.json +4204 -0
  4. package/dist/llms-full.txt +2839 -0
  5. package/dist/wovenui-kit.css +5002 -0
  6. package/dist/wovenui-kit.css.map +1 -0
  7. package/dist/wovenui-kit.iife.js +532 -0
  8. package/dist/wovenui-kit.js +529 -0
  9. package/dist/wovenui-kit.min.css +2 -0
  10. package/dist/wovenui-kit.min.css.map +1 -0
  11. package/package.json +268 -4
  12. package/src/_close.scss +45 -0
  13. package/src/_helpers.scss +11 -0
  14. package/src/_kit.scss +124 -0
  15. package/src/_shared.scss +10 -0
  16. package/src/components/_accordion.scss +132 -0
  17. package/src/components/_alert.scss +161 -0
  18. package/src/components/_autocomplete.scss +129 -0
  19. package/src/components/_avatar-group.scss +80 -0
  20. package/src/components/_avatar.scss +134 -0
  21. package/src/components/_badge.scss +91 -0
  22. package/src/components/_breadcrumb.scss +115 -0
  23. package/src/components/_btn-group.scss +113 -0
  24. package/src/components/_button.scss +301 -0
  25. package/src/components/_card.scss +120 -0
  26. package/src/components/_checkbox.scss +168 -0
  27. package/src/components/_code-block.scss +78 -0
  28. package/src/components/_confirm-modal.scss +75 -0
  29. package/src/components/_data-table.scss +79 -0
  30. package/src/components/_desc-list.scss +180 -0
  31. package/src/components/_divider.scss +66 -0
  32. package/src/components/_dot.scss +115 -0
  33. package/src/components/_drawer.scss +200 -0
  34. package/src/components/_dropdown.scss +182 -0
  35. package/src/components/_empty-state.scss +146 -0
  36. package/src/components/_field-group.scss +68 -0
  37. package/src/components/_field-message.scss +75 -0
  38. package/src/components/_field.scss +127 -0
  39. package/src/components/_form-actions.scss +58 -0
  40. package/src/components/_info-row.scss +69 -0
  41. package/src/components/_input-group.scss +87 -0
  42. package/src/components/_input.scss +278 -0
  43. package/src/components/_list.scss +76 -0
  44. package/src/components/_modal.scss +168 -0
  45. package/src/components/_page-header.scss +85 -0
  46. package/src/components/_pagination.scss +138 -0
  47. package/src/components/_panel.scss +69 -0
  48. package/src/components/_popover.scss +154 -0
  49. package/src/components/_progress-circle.scss +123 -0
  50. package/src/components/_progress.scss +122 -0
  51. package/src/components/_radio.scss +162 -0
  52. package/src/components/_segmented.scss +183 -0
  53. package/src/components/_select.scss +154 -0
  54. package/src/components/_sidebar.scss +300 -0
  55. package/src/components/_skeleton.scss +90 -0
  56. package/src/components/_spinner.scss +84 -0
  57. package/src/components/_stat.scss +92 -0
  58. package/src/components/_table.scss +460 -0
  59. package/src/components/_tabs.scss +327 -0
  60. package/src/components/_tag.scss +143 -0
  61. package/src/components/_text.scss +42 -0
  62. package/src/components/_textarea.scss +57 -0
  63. package/src/components/_timeline.scss +196 -0
  64. package/src/components/_title.scss +41 -0
  65. package/src/components/_toast.scss +221 -0
  66. package/src/components/_toggle.scss +171 -0
  67. package/src/components/_toolbar.scss +74 -0
  68. package/src/components/_topbar.scss +204 -0
  69. package/src/entries/accordion.scss +4 -0
  70. package/src/entries/alert.scss +4 -0
  71. package/src/entries/autocomplete.scss +4 -0
  72. package/src/entries/avatar-group.scss +4 -0
  73. package/src/entries/avatar.scss +4 -0
  74. package/src/entries/badge.scss +4 -0
  75. package/src/entries/breadcrumb.scss +4 -0
  76. package/src/entries/btn-group.scss +4 -0
  77. package/src/entries/button.scss +4 -0
  78. package/src/entries/card.scss +4 -0
  79. package/src/entries/checkbox.scss +4 -0
  80. package/src/entries/code-block.scss +4 -0
  81. package/src/entries/confirm-modal.scss +4 -0
  82. package/src/entries/data-table.scss +4 -0
  83. package/src/entries/desc-list.scss +4 -0
  84. package/src/entries/divider.scss +4 -0
  85. package/src/entries/dot.scss +4 -0
  86. package/src/entries/drawer.scss +4 -0
  87. package/src/entries/dropdown.scss +4 -0
  88. package/src/entries/empty-state.scss +4 -0
  89. package/src/entries/field-group.scss +4 -0
  90. package/src/entries/field-message.scss +4 -0
  91. package/src/entries/field.scss +4 -0
  92. package/src/entries/form-actions.scss +4 -0
  93. package/src/entries/info-row.scss +4 -0
  94. package/src/entries/input-group.scss +4 -0
  95. package/src/entries/input.scss +4 -0
  96. package/src/entries/list.scss +4 -0
  97. package/src/entries/modal.scss +4 -0
  98. package/src/entries/page-header.scss +4 -0
  99. package/src/entries/pagination.scss +4 -0
  100. package/src/entries/panel.scss +4 -0
  101. package/src/entries/popover.scss +4 -0
  102. package/src/entries/progress-circle.scss +4 -0
  103. package/src/entries/progress.scss +4 -0
  104. package/src/entries/radio.scss +4 -0
  105. package/src/entries/segmented.scss +4 -0
  106. package/src/entries/select.scss +4 -0
  107. package/src/entries/sidebar.scss +4 -0
  108. package/src/entries/skeleton.scss +4 -0
  109. package/src/entries/spinner.scss +4 -0
  110. package/src/entries/stat.scss +4 -0
  111. package/src/entries/table.scss +4 -0
  112. package/src/entries/tabs.scss +4 -0
  113. package/src/entries/tag.scss +4 -0
  114. package/src/entries/text.scss +4 -0
  115. package/src/entries/textarea.scss +4 -0
  116. package/src/entries/timeline.scss +4 -0
  117. package/src/entries/title.scss +4 -0
  118. package/src/entries/toast.scss +4 -0
  119. package/src/entries/toggle.scss +4 -0
  120. package/src/entries/toolbar.scss +4 -0
  121. package/src/entries/topbar.scss +4 -0
  122. package/src/index.scss +7 -0
  123. package/src/js/autocomplete.js +86 -0
  124. package/src/js/data-table.js +141 -0
  125. package/src/js/index.js +34 -0
  126. package/src/js/input-clear.js +25 -0
  127. package/src/js/package.json +3 -0
  128. package/src/js/tabs.js +73 -0
  129. package/src/js/toast.js +161 -0
  130. package/src/js/util.js +21 -0
@@ -0,0 +1,161 @@
1
+ ////
2
+ //// @component wu-alert
3
+ //// Inline message with a shape marker, a title, optional actions and a dismiss button.
4
+ ////
5
+ //// Meaning is carried by the `wu-alert-title` text, such as "Error" or "Saved"; the marker shape repeats it.
6
+ ////
7
+ //// Use `role="alert"` for urgent messages and `role="status"` for the rest. The close button is markup only; the app removes the alert.
8
+ ////
9
+ //// @since 1.0.0
10
+ //// @needs none
11
+ //// @js none
12
+ //// @example html
13
+ //// <div class="wu-alert success" role="status">
14
+ //// <strong class="wu-alert-title">Saved</strong>
15
+ //// <p>Your changes are live.</p>
16
+ //// </div>
17
+ //// @example html
18
+ //// <div class="wu-alert danger" role="alert">
19
+ //// <strong class="wu-alert-title">Payment failed</strong>
20
+ //// <p>Check the card details and try again.</p>
21
+ //// <div class="wu-alert-actions">
22
+ //// <button class="wu-btn danger" type="button">Retry</button>
23
+ //// <button class="wu-btn" type="button">Cancel</button>
24
+ //// </div>
25
+ //// <button class="wu-alert-close" type="button" aria-label="Dismiss"></button>
26
+ //// </div>
27
+ //// @modifier brand | success | danger | warning | info - Role color for border, fill, marker and title; `success`, `warning` and `danger` also change the marker shape @since 1.0.0
28
+ //// @part wu-alert-title - Bold title line that names the message @since 1.0.0
29
+ //// @part wu-alert-actions - Row of buttons under the text @since 1.0.0
30
+ //// @part wu-alert-close - Dismiss button in the end corner, last child of the alert @since 1.0.0
31
+ ////
32
+
33
+ @use "@wovenui/core/mixins" as woven;
34
+
35
+ .wu-alert {
36
+ position: relative;
37
+ padding-block: woven.token(space-3);
38
+ padding-inline: calc(#{woven.token(space-4)} * 2 + #{woven.token(space-3)}) woven.token(space-4);
39
+ border: woven.token(border-width-1) solid woven.token(gray-border);
40
+ border-radius: woven.token(radius-md);
41
+ background: woven.token(gray-subtle);
42
+ color: woven.token(text-regular);
43
+ font-size: woven.token(font-size-sm);
44
+ line-height: woven.token(line-height-normal);
45
+
46
+ // Marker, centered on the first line: circle by default
47
+ &::before {
48
+ content: "";
49
+ position: absolute;
50
+ inset-block-start: calc(#{woven.token(space-3)} + (1lh - #{woven.token(space-4)}) / 2);
51
+ inset-inline-start: woven.token(space-4);
52
+ width: woven.token(space-4);
53
+ height: woven.token(space-4);
54
+ border-radius: woven.token(radius-full);
55
+ background: woven.token(text-gray);
56
+ }
57
+
58
+ > * {
59
+ margin: 0;
60
+ }
61
+
62
+ > * + * {
63
+ margin-block-start: woven.token(space-1);
64
+ }
65
+ }
66
+
67
+ .wu-alert-title {
68
+ display: block;
69
+ font-weight: woven.token(font-weight-semibold);
70
+ }
71
+
72
+ @each $role in "brand", "success", "danger", "warning", "info" {
73
+ .wu-alert.#{$role} {
74
+ border-color: woven.token(#{$role}-border);
75
+ background: woven.token(#{$role}-subtle);
76
+
77
+ &::before {
78
+ background: woven.token(text-#{$role});
79
+ }
80
+
81
+ .wu-alert-title {
82
+ color: woven.token(text-#{$role});
83
+ }
84
+ }
85
+ }
86
+
87
+ // Shape markers from _shared.scss
88
+ .wu-alert:is(.success, .warning, .danger)::before {
89
+ border-radius: 0;
90
+ }
91
+
92
+ .wu-alert.success::before {
93
+ clip-path: var(--wu-kit-marker-check);
94
+ }
95
+
96
+ .wu-alert.warning::before {
97
+ clip-path: var(--wu-kit-marker-triangle);
98
+ }
99
+
100
+ .wu-alert.danger::before {
101
+ clip-path: var(--wu-kit-marker-octagon);
102
+ }
103
+
104
+ // Buttons under the text
105
+ .wu-alert > .wu-alert-actions {
106
+ display: flex;
107
+ flex-wrap: wrap;
108
+ gap: woven.token(space-2);
109
+ margin-block-start: woven.token(space-3);
110
+ }
111
+
112
+ // Dismiss: <button class="wu-alert-close" type="button" aria-label="Dismiss">, last in the alert.
113
+ // Markup only; the app removes the alert. Cross drawn like .wu-tag-remove.
114
+ .wu-alert:has(> .wu-alert-close) {
115
+ padding-inline-end: calc(#{woven.token(space-2)} + #{woven.token(space-6)} + #{woven.token(space-2)});
116
+ }
117
+
118
+ .wu-alert > .wu-alert-close {
119
+ position: absolute;
120
+ inset-block-start: calc(#{woven.token(space-3)} + (1lh - #{woven.token(space-6)}) / 2);
121
+ inset-inline-end: woven.token(space-2);
122
+ display: inline-grid;
123
+ place-items: center;
124
+ width: woven.token(space-6);
125
+ height: woven.token(space-6);
126
+ min-height: 0;
127
+ margin: 0;
128
+ padding: 0;
129
+ border: 0;
130
+ border-radius: woven.token(radius-xs);
131
+ background: transparent;
132
+ color: woven.token(text-muted);
133
+ font: inherit;
134
+ line-height: 1;
135
+ cursor: pointer;
136
+
137
+ &::before,
138
+ &::after {
139
+ content: "";
140
+ grid-area: 1 / 1;
141
+ width: woven.token(space-3);
142
+ height: woven.token(border-width-2);
143
+ border-radius: woven.token(radius-full);
144
+ background: currentcolor;
145
+ rotate: 45deg;
146
+ }
147
+
148
+ &::after {
149
+ rotate: -45deg;
150
+ }
151
+
152
+ &:hover {
153
+ background: color-mix(in srgb, currentcolor 15%, transparent);
154
+ color: woven.token(text-regular);
155
+ }
156
+
157
+ &:focus-visible {
158
+ outline: woven.token(border-focus-width) solid woven.token(border-focus);
159
+ outline-offset: woven.token(border-focus-offset);
160
+ }
161
+ }
@@ -0,0 +1,129 @@
1
+ ////
2
+ //// @component wu-autocomplete
3
+ //// Combobox that filters a list of options as the user types.
4
+ ////
5
+ //// The kit script filters the options, handles the keyboard and sets `aria-expanded`, `aria-activedescendant`, `aria-selected` and `hidden`.
6
+ ////
7
+ //// @since 1.0.0
8
+ //// @needs input
9
+ //// @js required initAutocomplete
10
+ //// @example html
11
+ //// <div class="wu-autocomplete">
12
+ //// <label for="city">City</label>
13
+ //// <input class="wu-input" id="city" type="text" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="city-list" autocomplete="off">
14
+ //// <ul class="wu-autocomplete-list" id="city-list" role="listbox" aria-label="Cities" hidden>
15
+ //// <li class="wu-autocomplete-option" id="city-1" role="option">Amsterdam</li>
16
+ //// <li class="wu-autocomplete-option" id="city-2" role="option">Berlin</li>
17
+ //// <li class="wu-autocomplete-option" id="city-3" role="option" aria-disabled="true">Closed city</li>
18
+ //// </ul>
19
+ //// <p class="wu-autocomplete-empty" hidden>No matches</p>
20
+ //// </div>
21
+ //// @modifier animated - The popup fades and slides in when the user allows motion; `wu-animated` on an ancestor does the same @since 1.0.0
22
+ //// @part wu-autocomplete-list - Popup `ul` that holds the options @since 1.0.0
23
+ //// @part wu-autocomplete-option - One selectable `li` option @since 1.0.0
24
+ //// @part wu-autocomplete-empty - Message shown when no option matches @since 1.0.0
25
+ //// @variable --wu-autocomplete--MaxHeight [16rem] - Maximum height of the option list before it scrolls @since 1.0.0
26
+ //// @state aria-selected="true" - The active option: selected fill and a start bar @since 1.0.0
27
+ //// @state aria-disabled="true" - Half opacity and a not-allowed cursor @since 1.0.0
28
+ //// @state hidden - An option filtered out is not displayed @since 1.0.0
29
+ ////
30
+
31
+ @use "@wovenui/core/mixins" as woven;
32
+
33
+ .wu-autocomplete {
34
+ position: relative;
35
+
36
+ > .wu-input {
37
+ inline-size: 100%;
38
+ }
39
+ }
40
+
41
+ .wu-autocomplete-list,
42
+ .wu-autocomplete-empty {
43
+ position: absolute;
44
+ inset-inline: 0;
45
+ inset-block-start: calc(100% + #{woven.token(space-1)});
46
+ z-index: woven.token(z-dropdown);
47
+ margin: 0;
48
+ padding: woven.token(space-1);
49
+ border: woven.token(border-width-1) solid woven.token(border-regular);
50
+ border-radius: woven.token(radius-md);
51
+ background: woven.token(bg-raised);
52
+ color: woven.token(text-regular);
53
+ font-size: woven.token(font-size-sm);
54
+ box-shadow: woven.token(shadow-lg);
55
+ }
56
+
57
+ .wu-autocomplete-list {
58
+ max-block-size: var(--wu-autocomplete--MaxHeight, 16rem);
59
+ list-style: none;
60
+ overflow-y: auto;
61
+ overscroll-behavior: contain;
62
+ }
63
+
64
+ .wu-autocomplete-empty {
65
+ padding-block: woven.token(space-2);
66
+ padding-inline: woven.token(space-3);
67
+ color: woven.token(text-muted);
68
+ }
69
+
70
+ .wu-autocomplete-option {
71
+ display: flex;
72
+ align-items: center;
73
+ min-block-size: woven.token(control-height-sm);
74
+ padding-inline: woven.token(space-3);
75
+ border-radius: woven.token(radius-sm);
76
+ cursor: pointer;
77
+
78
+ &[hidden] {
79
+ display: none;
80
+ }
81
+
82
+ &:hover {
83
+ background: woven.token(bg-hover);
84
+ }
85
+
86
+ // Active option (aria-activedescendant): selected background and a start bar, so color is not the only signal
87
+ &[aria-selected="true"] {
88
+ background: woven.token(bg-selected);
89
+ box-shadow: inset woven.token(border-width-2) 0 0 woven.token(brand);
90
+ }
91
+
92
+ &[aria-disabled="true"] {
93
+ opacity: woven.token(opacity-50);
94
+ cursor: not-allowed;
95
+ }
96
+ }
97
+
98
+ .wu-autocomplete-option[aria-selected="true"]:dir(rtl) {
99
+ box-shadow: inset calc(-1 * #{woven.token(border-width-2)}) 0 0 woven.token(brand);
100
+ }
101
+
102
+ // Opt-in motion: the popup fades and slides in
103
+ @media (prefers-reduced-motion: no-preference) {
104
+ .wu-autocomplete.animated > :is(.wu-autocomplete-list, .wu-autocomplete-empty),
105
+ .wu-animated .wu-autocomplete > :is(.wu-autocomplete-list, .wu-autocomplete-empty) {
106
+ transition:
107
+ opacity woven.token(duration-150) woven.token(ease-out),
108
+ translate woven.token(duration-150) woven.token(ease-out);
109
+
110
+ @starting-style {
111
+ opacity: 0;
112
+ translate: 0 calc(-1 * #{woven.token(space-1)});
113
+ }
114
+ }
115
+ }
116
+
117
+ @media (forced-colors: active) {
118
+ .wu-autocomplete-option[aria-selected="true"] {
119
+ background: Highlight;
120
+ color: HighlightText;
121
+ forced-color-adjust: none;
122
+ }
123
+ }
124
+
125
+ .wu-autocomplete-option > svg {
126
+ flex-shrink: 0;
127
+ width: 1em;
128
+ height: 1em;
129
+ }
@@ -0,0 +1,80 @@
1
+ ////
2
+ //// @component wu-avatar-group
3
+ //// Overlapping stack of avatars with a ring between them.
4
+ ////
5
+ //// Put the overflow count, such as "+3", in a last `wu-avatar` as real text.
6
+ ////
7
+ //// @since 1.0.0
8
+ //// @needs avatar
9
+ //// @js none
10
+ //// @example html
11
+ //// <div class="wu-avatar-group" role="group" aria-label="Members">
12
+ //// <span class="wu-avatar blue" role="img" aria-label="Ana Lopez">AL</span>
13
+ //// <span class="wu-avatar green" role="img" aria-label="Ben Kim">BK</span>
14
+ //// <span class="wu-avatar" role="img" aria-label="3 more members">+3</span>
15
+ //// </div>
16
+ //// @example html
17
+ //// <div class="wu-avatar-group animated" role="group" aria-label="Team">
18
+ //// <span class="wu-avatar purple" role="img" aria-label="Cy Ray">CR</span>
19
+ //// <span class="wu-avatar pink" role="img" aria-label="Di Eze">DE</span>
20
+ //// </div>
21
+ //// @modifier animated - On hover or focus within, the stack fans out into a row when the user allows motion; `wu-animated` on an ancestor does the same @since 1.0.0
22
+ ////
23
+
24
+ @use "@wovenui/core/mixins" as woven;
25
+
26
+ $overlap: woven.token(space-3);
27
+ $fan-step: calc(#{$overlap} + #{woven.token(space-1)});
28
+
29
+ .wu-avatar-group {
30
+ display: inline-flex;
31
+ align-items: center;
32
+ vertical-align: middle;
33
+
34
+ > .wu-avatar {
35
+ box-shadow: 0 0 0 woven.token(border-width-2) var(--wu-avatar--RingColor, #{woven.token(bg-surface)});
36
+ }
37
+
38
+ > .wu-avatar + .wu-avatar {
39
+ margin-inline-start: calc(-1 * #{$overlap});
40
+ }
41
+ }
42
+
43
+ // Fan-out offsets: relative inset moves only the avatar, never the layout around it
44
+ @mixin fan-out {
45
+ @supports (inset-inline-start: calc(sibling-index() * 1px)) {
46
+ > .wu-avatar {
47
+ inset-inline-start: calc((sibling-index() - 1) * #{$fan-step});
48
+ }
49
+ }
50
+
51
+ @supports not (inset-inline-start: calc(sibling-index() * 1px)) {
52
+ @for $i from 2 through 12 {
53
+ > .wu-avatar:nth-child(#{$i}) {
54
+ inset-inline-start: calc(#{$i - 1} * #{$fan-step});
55
+ }
56
+ }
57
+ }
58
+ }
59
+
60
+ // Opt-in motion: the stack fans out into a row as a visual effect, staggered where sibling-index() is supported
61
+ @media (prefers-reduced-motion: no-preference) {
62
+ .wu-avatar-group.animated,
63
+ .wu-animated .wu-avatar-group {
64
+ > .wu-avatar {
65
+ inset-inline-start: 0;
66
+ transition: inset-inline-start woven.token(duration-500) var(--wu-kit-ease-spring);
67
+ transition-delay: calc((sibling-index() - 1) * 30ms);
68
+ }
69
+
70
+ &:focus-within {
71
+ @include fan-out;
72
+ }
73
+
74
+ @media (hover: hover) {
75
+ &:hover {
76
+ @include fan-out;
77
+ }
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,134 @@
1
+ ////
2
+ //// @component wu-avatar
3
+ //// Round or square badge for a person or product, with initials, an image or an svg, and an optional status dot.
4
+ ////
5
+ //// Label it with `aria-label` or the image `alt`, status included.
6
+ ////
7
+ //// `--wu-avatar--Size` overrides every size.
8
+ ////
9
+ //// @since 1.0.0
10
+ //// @needs none
11
+ //// @js none
12
+ //// @example html
13
+ //// <span class="wu-avatar" role="img" aria-label="Ana Lopez">AL</span>
14
+ //// <span class="wu-avatar sm blue" role="img" aria-label="Ben Kim">BK</span>
15
+ //// <span class="wu-avatar lg purple" role="img" aria-label="Cy Ray">CR</span>
16
+ //// @example html
17
+ //// <span class="wu-avatar green" data-status="online" role="img" aria-label="Di Eze, online">DE</span>
18
+ //// <span class="wu-avatar tile"><img src="https://wovenui.com/images/logo.svg" alt="WovenUI"></span>
19
+ //// @modifier sm | lg - Small or large size; the default is medium @since 1.0.0
20
+ //// @modifier tile - Square with rounded corners, for company and product logos @since 1.0.0
21
+ //// @modifier gray | red | blue | green | yellow | purple | pink - Tinted fill and text in that hue @since 1.0.0
22
+ //// @variable --wu-avatar--Size [var(--wu-space-10)] - Width and height; initials scale with it @since 1.0.0
23
+ //// @variable --wu-avatar--RingColor [var(--wu-bg-surface)] - Color of the ring around the status dot, shared with avatar group and corner badge @since 1.0.0
24
+ //// @state data-status="online" - Solid dot at the end corner @since 1.0.0
25
+ //// @state data-status="away" - Half-filled dot @since 1.0.0
26
+ //// @state data-status="busy" - Filled dot with a bar @since 1.0.0
27
+ //// @state data-status="offline" - Hollow ring @since 1.0.0
28
+ ////
29
+
30
+ @use "@wovenui/core/mixins" as woven;
31
+
32
+ $hues: "gray", "red", "blue", "green", "yellow", "purple", "pink";
33
+
34
+ // Size and initials scale from one value; --wu-avatar--Size overrides every size
35
+ @mixin size($default) {
36
+ width: var(--wu-avatar--Size, #{$default});
37
+ height: var(--wu-avatar--Size, #{$default});
38
+ font-size: calc(var(--wu-avatar--Size, #{$default}) * 0.4);
39
+ }
40
+
41
+ .wu-avatar {
42
+ display: inline-grid;
43
+ place-items: center;
44
+ position: relative;
45
+ flex-shrink: 0;
46
+ border-radius: woven.token(radius-full);
47
+ background: woven.token(gray-subtle);
48
+ color: woven.token(text-gray);
49
+ font-weight: woven.token(font-weight-medium);
50
+ line-height: 1;
51
+ text-transform: uppercase;
52
+ white-space: nowrap;
53
+ vertical-align: middle;
54
+ user-select: none;
55
+
56
+ @include size(woven.token(space-10));
57
+
58
+ > img {
59
+ width: 100%;
60
+ height: 100%;
61
+ border-radius: inherit;
62
+ object-fit: cover;
63
+ }
64
+ }
65
+
66
+ .wu-avatar.sm {
67
+ @include size(woven.token(space-8));
68
+ }
69
+
70
+ .wu-avatar.lg {
71
+ @include size(woven.token(space-12));
72
+ }
73
+
74
+ // Square: company and product logos
75
+ .wu-avatar.tile {
76
+ border-radius: woven.token(radius-md);
77
+ }
78
+
79
+ // App svg as the glyph
80
+ .wu-avatar > svg {
81
+ width: 60%;
82
+ height: 60%;
83
+ }
84
+
85
+ @each $hue in $hues {
86
+ .wu-avatar.#{$hue} {
87
+ background: woven.token(#{$hue}-subtle);
88
+ color: woven.token(text-#{$hue});
89
+ }
90
+ }
91
+
92
+ // Status indicator: each state has its own shape, not only a color
93
+ .wu-avatar:is([data-status="online"], [data-status="away"], [data-status="busy"], [data-status="offline"])::after {
94
+ content: "";
95
+ position: absolute;
96
+ inset-block-end: 0;
97
+ inset-inline-end: 0;
98
+ width: 30%;
99
+ height: 30%;
100
+ border-radius: woven.token(radius-full);
101
+ }
102
+
103
+ // Online: solid dot
104
+ .wu-avatar[data-status="online"]::after {
105
+ background: woven.token(success);
106
+ box-shadow: 0 0 0 woven.token(border-width-2) var(--wu-avatar--RingColor, #{woven.token(bg-surface)});
107
+ }
108
+
109
+ // Away: half filled
110
+ .wu-avatar[data-status="away"]::after {
111
+ background: linear-gradient(to right, woven.token(warning) 50%, var(--wu-avatar--RingColor, #{woven.token(bg-surface)}) 50%);
112
+ box-shadow:
113
+ inset 0 0 0 woven.token(border-width-1) woven.token(warning),
114
+ 0 0 0 woven.token(border-width-2) var(--wu-avatar--RingColor, #{woven.token(bg-surface)});
115
+ }
116
+
117
+ .wu-avatar[data-status="away"]:dir(rtl)::after {
118
+ background: linear-gradient(to left, woven.token(warning) 50%, var(--wu-avatar--RingColor, #{woven.token(bg-surface)}) 50%);
119
+ }
120
+
121
+ // Busy: filled with a bar
122
+ .wu-avatar[data-status="busy"]::after {
123
+ background:
124
+ linear-gradient(var(--wu-avatar--RingColor, #{woven.token(bg-surface)}), var(--wu-avatar--RingColor, #{woven.token(bg-surface)})) center / 60% 20% no-repeat,
125
+ woven.token(danger);
126
+ }
127
+
128
+ // Offline: hollow ring
129
+ .wu-avatar[data-status="offline"]::after {
130
+ background: var(--wu-avatar--RingColor, #{woven.token(bg-surface)});
131
+ box-shadow:
132
+ inset 0 0 0 woven.token(border-width-2) woven.token(border-strong),
133
+ 0 0 0 woven.token(border-width-2) var(--wu-avatar--RingColor, #{woven.token(bg-surface)});
134
+ }
@@ -0,0 +1,91 @@
1
+ ////
2
+ //// @component wu-badge
3
+ //// Small pill for counts and short labels, tinted by default and solid for emphasis.
4
+ ////
5
+ //// @since 1.0.0
6
+ //// @needs none
7
+ //// @js none
8
+ //// @example html
9
+ //// <span class="wu-badge">Draft</span>
10
+ //// <span class="wu-badge success">Paid</span>
11
+ //// <span class="wu-badge danger solid">3</span>
12
+ //// @example html
13
+ //// <span class="wu-badge-host">
14
+ //// <button class="wu-btn" type="button" aria-label="Inbox, 3 unread">Inbox</button>
15
+ //// <span class="wu-badge corner danger solid">3</span>
16
+ //// </span>
17
+ //// @modifier brand | success | danger | warning | info - Role color: tinted fill, border and text; solid fill with `solid` @since 1.0.0
18
+ //// @modifier solid - Solid fill for stronger emphasis, gray without a role @since 1.0.0
19
+ //// @modifier corner - Pins the badge to the top inline-end corner of a `wu-badge-host` @since 1.0.0
20
+ //// @part wu-badge-host - Wrapper that a corner badge pins to @since 1.0.0
21
+ //// @variable --wu-badge--Offset [50%] - How far a corner badge sits outside the host corner @since 1.0.0
22
+ //// @variable --wu-avatar--RingColor [var(--wu-bg-surface)] - Color of the ring around a corner badge, shared with avatar @since 1.0.0
23
+ //// @state :empty - A corner badge with no content is hidden @since 1.0.0
24
+ ////
25
+
26
+ @use "@wovenui/core/mixins" as woven;
27
+
28
+ $roles: "brand", "success", "danger", "warning", "info";
29
+
30
+ .wu-badge {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ gap: woven.token(space-1);
34
+ min-height: woven.token(space-5);
35
+ padding-inline: woven.token(space-2);
36
+ border: woven.token(border-width-1) solid woven.token(gray-border);
37
+ border-radius: woven.token(radius-full);
38
+ background: woven.token(gray-subtle);
39
+ color: woven.token(text-gray);
40
+ font-size: woven.token(font-size-xs);
41
+ font-variant-numeric: tabular-nums;
42
+ font-weight: woven.token(font-weight-medium);
43
+ line-height: 1;
44
+ white-space: nowrap;
45
+ vertical-align: middle;
46
+ }
47
+
48
+ @each $role in $roles {
49
+ .wu-badge.#{$role} {
50
+ border-color: woven.token(#{$role}-border);
51
+ background: woven.token(#{$role}-subtle);
52
+ color: woven.token(text-#{$role});
53
+ }
54
+ }
55
+
56
+ // Solid fill for stronger emphasis
57
+ .wu-badge.solid {
58
+ border-color: woven.token(gray-700);
59
+ background: woven.token(gray-700);
60
+ color: woven.token(white);
61
+ }
62
+
63
+ @each $role in $roles {
64
+ .wu-badge.solid.#{$role} {
65
+ border-color: woven.token($role);
66
+ background: woven.token($role);
67
+ color: woven.token(text-on-#{$role});
68
+ }
69
+ }
70
+
71
+ .wu-badge-host {
72
+ display: inline-flex;
73
+ position: relative;
74
+ vertical-align: middle;
75
+ }
76
+
77
+ .wu-badge.corner {
78
+ position: absolute;
79
+ inset-block-start: 0;
80
+ inset-inline-end: 0;
81
+ translate: var(--wu-badge--Offset, 50%) -50%;
82
+ box-shadow: 0 0 0 woven.token(border-width-2) var(--wu-avatar--RingColor, #{woven.token(bg-surface)});
83
+
84
+ &:dir(rtl) {
85
+ translate: calc(-1 * var(--wu-badge--Offset, 50%)) -50%;
86
+ }
87
+
88
+ &:empty {
89
+ display: none;
90
+ }
91
+ }