@realmorg/ui 0.0.0-stage → 2.0.0-alpha.3

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 (66) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +44 -2
  3. package/components/accordion.html +139 -0
  4. package/components/alert-dialog.html +236 -0
  5. package/components/alert.html +83 -0
  6. package/components/aspect-ratio.html +29 -0
  7. package/components/attachment.html +98 -0
  8. package/components/avatar.html +149 -0
  9. package/components/badge.html +62 -0
  10. package/components/breadcrumb.html +159 -0
  11. package/components/bubble.html +77 -0
  12. package/components/button-group.html +98 -0
  13. package/components/button.html +194 -0
  14. package/components/calendar.html +176 -0
  15. package/components/card.html +143 -0
  16. package/components/carousel.html +208 -0
  17. package/components/chart.html +168 -0
  18. package/components/checkbox.html +99 -0
  19. package/components/code-block.html +64 -0
  20. package/components/collapsible.html +90 -0
  21. package/components/combobox.html +237 -0
  22. package/components/command.html +201 -0
  23. package/components/context-menu.html +81 -0
  24. package/components/dialog.html +327 -0
  25. package/components/direction.html +23 -0
  26. package/components/drawer.html +267 -0
  27. package/components/dropdown-menu.html +421 -0
  28. package/components/empty.html +132 -0
  29. package/components/field.html +143 -0
  30. package/components/hover-card.html +111 -0
  31. package/components/input-group.html +91 -0
  32. package/components/input-otp.html +151 -0
  33. package/components/input.html +94 -0
  34. package/components/item.html +208 -0
  35. package/components/kbd.html +45 -0
  36. package/components/label.html +46 -0
  37. package/components/marker.html +41 -0
  38. package/components/menubar.html +137 -0
  39. package/components/message-scroller.html +68 -0
  40. package/components/message.html +95 -0
  41. package/components/navigation-menu.html +233 -0
  42. package/components/pagination.html +160 -0
  43. package/components/popover.html +164 -0
  44. package/components/progress.html +45 -0
  45. package/components/radio-group.html +120 -0
  46. package/components/resizable.html +170 -0
  47. package/components/scroll-area.html +56 -0
  48. package/components/select.html +103 -0
  49. package/components/separator.html +31 -0
  50. package/components/sheet.html +173 -0
  51. package/components/sidebar.html +393 -0
  52. package/components/skeleton.html +36 -0
  53. package/components/slider.html +108 -0
  54. package/components/spinner.html +53 -0
  55. package/components/switch.html +95 -0
  56. package/components/table.html +81 -0
  57. package/components/tabs.html +182 -0
  58. package/components/textarea.html +80 -0
  59. package/components/toaster.html +134 -0
  60. package/components/toggle-group.html +71 -0
  61. package/components/toggle.html +87 -0
  62. package/components/tooltip.html +114 -0
  63. package/dist/theme.css +158 -0
  64. package/dist/ui.html +7923 -0
  65. package/package.json +40 -3
  66. package/theme.css +158 -0
@@ -0,0 +1,160 @@
1
+ <custom-element name="ui-pagination">
2
+ <script>
3
+ const props = defineProps({
4
+ page: prop.number().int().min(1).default(1),
5
+ total: prop.number().int().min(1).default(1),
6
+ siblings: prop.number().int().min(0).default(1),
7
+ href: prop.string(),
8
+ label: prop.string().default("Pagination"),
9
+ });
10
+
11
+ const pages = useState({
12
+ get items() {
13
+ const { page, total, siblings } = props;
14
+ const keep = new Set([1, total]);
15
+ for (let n = page - siblings; n <= page + siblings; n++) if (n >= 1 && n <= total) keep.add(n);
16
+ const sorted = [...keep].sort((a, b) => a - b);
17
+ return sorted.flatMap((n, index) => {
18
+ const item = { key: `p${n}`, number: n, kind: this.kind, current: n === page ? "page" : null, url: link(n) };
19
+ return index > 0 && n - sorted[index - 1] > 1 ? [{ key: `gap${n}`, kind: "gap" }, item] : [item];
20
+ });
21
+ },
22
+ get kind() {
23
+ return props.href ? "link" : "button";
24
+ },
25
+ get first() {
26
+ return props.page <= 1;
27
+ },
28
+ get last() {
29
+ return props.page >= props.total;
30
+ },
31
+ get previousUrl() {
32
+ return this.first ? null : link(props.page - 1);
33
+ },
34
+ get nextUrl() {
35
+ return this.last ? null : link(props.page + 1);
36
+ },
37
+ });
38
+
39
+ function link(n) {
40
+ return props.href ? props.href.replace("{page}", String(n)) : null;
41
+ }
42
+
43
+ function previous(event) {
44
+ go(props.page - 1, event);
45
+ }
46
+
47
+ function next(event) {
48
+ go(props.page + 1, event);
49
+ }
50
+
51
+ function go(page, event) {
52
+ if (page < 1 || page > props.total || page === props.page) return;
53
+ if (!dispatch("change", { page })) return event?.preventDefault();
54
+ if (!props.href) props.page = page;
55
+ }
56
+ </script>
57
+
58
+ <style>
59
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
60
+ touch-action does not reach shadow content in iOS Safari. */
61
+ :host,
62
+ * {
63
+ touch-action: manipulation;
64
+ }
65
+ :host {
66
+ display: flex;
67
+ justify-content: center;
68
+ }
69
+ ul {
70
+ display: flex;
71
+ flex-wrap: wrap;
72
+ justify-content: center;
73
+ align-items: center;
74
+ gap: calc(var(--spacing) * 1);
75
+ margin: 0;
76
+ padding: 0;
77
+ list-style: none;
78
+ }
79
+ a,
80
+ button {
81
+ all: unset;
82
+ box-sizing: border-box;
83
+ display: inline-flex;
84
+ align-items: center;
85
+ justify-content: center;
86
+ gap: calc(var(--spacing) * 1);
87
+ min-width: calc(var(--spacing) * 9);
88
+ height: calc(var(--spacing) * 9);
89
+ padding: 0 calc(var(--spacing) * 2.5);
90
+ border: 1px solid transparent;
91
+ border-radius: var(--radius-md);
92
+ color: inherit;
93
+ font-size: var(--text-sm);
94
+ font-weight: var(--font-weight-medium);
95
+ text-decoration: none;
96
+ cursor: pointer;
97
+ }
98
+ :is(a, button):hover {
99
+ background: var(--accent);
100
+ }
101
+ :is(a, button):focus-visible {
102
+ border-color: var(--ring);
103
+ box-shadow: var(--focus-ring);
104
+ }
105
+ [aria-current="page"] {
106
+ border-color: var(--border);
107
+ box-shadow: var(--shadow-xs);
108
+ }
109
+ a:not([href]),
110
+ button:disabled {
111
+ pointer-events: none;
112
+ opacity: 0.5;
113
+ }
114
+ @media (width < 40rem) {
115
+ .label {
116
+ position: absolute;
117
+ width: 1px;
118
+ height: 1px;
119
+ overflow: hidden;
120
+ clip-path: inset(50%);
121
+ white-space: nowrap;
122
+ }
123
+ }
124
+ .gap {
125
+ display: inline-flex;
126
+ width: calc(var(--spacing) * 9);
127
+ justify-content: center;
128
+ }
129
+ </style>
130
+
131
+ <nav _aria-label="@label">
132
+ <ul part="list">
133
+ <li>
134
+ <is-visible when="#pages.kind" eq="link">
135
+ <a part="previous" _href="#pages.previousUrl" _aria-disabled="#pages.first" onclick="previous(event)">‹ <span class="label">Previous</span></a>
136
+ </is-visible>
137
+ <is-visible when="#pages.kind" eq="button">
138
+ <button type="button" part="previous" _disabled="#pages.first" onclick="previous(event)">‹ <span class="label">Previous</span></button>
139
+ </is-visible>
140
+ </li>
141
+ <li repeat="#pages.items" as="item" key="key">
142
+ <is-visible when="#item.kind" eq="link">
143
+ <a part="page" _href="#item.url" _aria-current="#item.current" onclick="go(item.number, event)"><slot name="#item.number"></slot></a>
144
+ </is-visible>
145
+ <is-visible when="#item.kind" eq="button">
146
+ <button type="button" part="page" _aria-current="#item.current" onclick="go(item.number, event)"><slot name="#item.number"></slot></button>
147
+ </is-visible>
148
+ <is-visible when="#item.kind" eq="gap"><span class="gap" aria-hidden="true">…</span></is-visible>
149
+ </li>
150
+ <li>
151
+ <is-visible when="#pages.kind" eq="link">
152
+ <a part="next" _href="#pages.nextUrl" _aria-disabled="#pages.last" onclick="next(event)"><span class="label">Next</span> ›</a>
153
+ </is-visible>
154
+ <is-visible when="#pages.kind" eq="button">
155
+ <button type="button" part="next" _disabled="#pages.last" onclick="next(event)"><span class="label">Next</span> ›</button>
156
+ </is-visible>
157
+ </li>
158
+ </ul>
159
+ </nav>
160
+ </custom-element>
@@ -0,0 +1,164 @@
1
+ <custom-element name="ui-popover">
2
+ <script>
3
+ let wasOpen = false;
4
+
5
+ function press() {
6
+ wasOpen = host.querySelector(":scope > ui-popover-content").matches(":popover-open");
7
+ }
8
+
9
+ function toggle(trigger) {
10
+ const content = host.querySelector(":scope > ui-popover-content");
11
+ const open = wasOpen || content.matches(":popover-open");
12
+ wasOpen = false;
13
+ if (open) content.hidePopover();
14
+ else {
15
+ trigger.firstElementChild?.focus();
16
+ content.showPopover({ source: trigger });
17
+ }
18
+ }
19
+ </script>
20
+
21
+ <style>
22
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
23
+ touch-action does not reach shadow content in iOS Safari. */
24
+ :host,
25
+ * {
26
+ touch-action: manipulation;
27
+ }
28
+ :host {
29
+ display: contents;
30
+ }
31
+ </style>
32
+
33
+ <slot></slot>
34
+ </custom-element>
35
+
36
+ <custom-element name="ui-popover-trigger">
37
+ <style>
38
+ :host,
39
+ * {
40
+ touch-action: manipulation;
41
+ }
42
+ :host {
43
+ display: inline-block;
44
+ vertical-align: middle;
45
+ }
46
+ </style>
47
+
48
+ <span part="trigger" onpointerdown="press()" onclick="toggle(host)"><slot></slot></span>
49
+ </custom-element>
50
+
51
+ <custom-element name="ui-popover-content">
52
+ <script>
53
+ defineProps({
54
+ placement: prop.enum(["bottom", "top", "left", "right"]).default("bottom"),
55
+ });
56
+
57
+ attrs.popover ??= "auto";
58
+ </script>
59
+
60
+ <style>
61
+ :host,
62
+ * {
63
+ touch-action: manipulation;
64
+ }
65
+ :host {
66
+ box-sizing: border-box;
67
+ width: calc(var(--spacing) * 72);
68
+ margin: calc(var(--spacing) * 1);
69
+ padding: calc(var(--spacing) * 4);
70
+ border: 1px solid var(--border);
71
+ border-radius: var(--radius-md);
72
+ background: var(--popover);
73
+ color: var(--popover-foreground);
74
+ box-shadow: var(--shadow-md);
75
+ inset: auto;
76
+ height: fit-content;
77
+ max-height: calc(100svh - calc(var(--spacing) * 8));
78
+ overflow-y: auto;
79
+ position-area: bottom;
80
+ position-try-fallbacks: flip-block, flip-inline;
81
+ }
82
+ :host(:popover-open) {
83
+ display: grid;
84
+ /* Safari stretches a grid overlay to its max-height unless the rows are packed at the start. */
85
+ align-content: start;
86
+ gap: calc(var(--spacing) * 4);
87
+ }
88
+ :host(:state(attr-width)) {
89
+ width: calc(var(--spacing) * var(--attr-width));
90
+ }
91
+ :host(:state(attr-placement-is-top)) {
92
+ position-area: top;
93
+ }
94
+ :host(:state(attr-placement-is-left)) {
95
+ position-area: left;
96
+ }
97
+ :host(:state(attr-placement-is-right)) {
98
+ position-area: right;
99
+ }
100
+ @supports not (position-area: bottom) {
101
+ :host {
102
+ inset: 0;
103
+ margin: auto;
104
+ height: fit-content;
105
+ }
106
+ }
107
+ </style>
108
+
109
+ <slot></slot>
110
+ </custom-element>
111
+
112
+ <custom-element name="ui-popover-header">
113
+ <style>
114
+ :host,
115
+ * {
116
+ touch-action: manipulation;
117
+ }
118
+ :host {
119
+ display: grid;
120
+ gap: calc(var(--spacing) * 1);
121
+ }
122
+ </style>
123
+
124
+ <slot></slot>
125
+ </custom-element>
126
+
127
+ <custom-element name="ui-popover-title">
128
+ <style>
129
+ :host,
130
+ * {
131
+ touch-action: manipulation;
132
+ }
133
+ :host {
134
+ display: block;
135
+ }
136
+ h3 {
137
+ margin: 0;
138
+ font-size: var(--text-sm);
139
+ font-weight: var(--font-weight-medium);
140
+ line-height: 1;
141
+ }
142
+ </style>
143
+
144
+ <h3 part="title"><slot></slot></h3>
145
+ </custom-element>
146
+
147
+ <custom-element name="ui-popover-description">
148
+ <style>
149
+ :host,
150
+ * {
151
+ touch-action: manipulation;
152
+ }
153
+ :host {
154
+ display: block;
155
+ }
156
+ p {
157
+ margin: 0;
158
+ font-size: var(--text-sm);
159
+ color: var(--muted-foreground);
160
+ }
161
+ </style>
162
+
163
+ <p part="description"><slot></slot></p>
164
+ </custom-element>
@@ -0,0 +1,45 @@
1
+ <custom-element name="ui-progress">
2
+ <script>
3
+ defineProps({
4
+ value: prop.number().min(0),
5
+ max: prop.number().min(1).default(100),
6
+ label: prop.string().attribute("aria-label"),
7
+ });
8
+ </script>
9
+
10
+ <style>
11
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
12
+ touch-action does not reach shadow content in iOS Safari. */
13
+ :host,
14
+ * {
15
+ touch-action: manipulation;
16
+ }
17
+ :host {
18
+ display: block;
19
+ width: 100%;
20
+ box-sizing: border-box;
21
+ }
22
+ progress {
23
+ appearance: none;
24
+ display: block;
25
+ width: 100%;
26
+ height: calc(var(--spacing) * 2);
27
+ border: 0;
28
+ border-radius: var(--radius-full);
29
+ overflow: hidden;
30
+ background: color-mix(in oklab, var(--primary) 20%, transparent);
31
+ }
32
+ progress::-webkit-progress-bar {
33
+ background: transparent;
34
+ }
35
+ progress::-webkit-progress-value {
36
+ background: var(--primary);
37
+ transition: inline-size 0.2s;
38
+ }
39
+ progress::-moz-progress-bar {
40
+ background: var(--primary);
41
+ }
42
+ </style>
43
+
44
+ <progress part="bar" _value="@value" _max="@max" _aria-label="@label"><slot></slot></progress>
45
+ </custom-element>
@@ -0,0 +1,120 @@
1
+ <custom-element name="ui-radio-group" form-associated>
2
+ <script>
3
+ const props = defineProps({
4
+ value: prop.string(),
5
+ orientation: prop.enum(["vertical", "horizontal"]).default("vertical"),
6
+ disabled: prop.boolean(),
7
+ required: prop.boolean(),
8
+ });
9
+
10
+ const options = useChildren("option", ["value", "label", "disabled"]);
11
+ const group = useState({
12
+ get options() {
13
+ return options.map((option, index) => ({
14
+ id: `${index}:${option.value}`,
15
+ value: option.value,
16
+ label: option.label,
17
+ disabled: props.disabled || option.disabled,
18
+ checked: option.value === props.value,
19
+ }));
20
+ },
21
+ });
22
+
23
+ function choose(option) {
24
+ props.value = option.value;
25
+ dispatch("change");
26
+ }
27
+
28
+ onConnected(() => {
29
+ internals.role = "radiogroup";
30
+ });
31
+
32
+ observe(() => {
33
+ const missing = props.required && !props.value;
34
+ const anchor = refs.list?.querySelector("input:not(:disabled)") ?? undefined;
35
+ internals.setValidity(missing ? { valueMissing: true } : {}, missing ? "Please select an option." : "", anchor);
36
+ });
37
+ </script>
38
+
39
+ <style>
40
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
41
+ touch-action does not reach shadow content in iOS Safari. */
42
+ :host,
43
+ * {
44
+ touch-action: manipulation;
45
+ }
46
+ :host {
47
+ display: grid;
48
+ gap: calc(var(--spacing) * 3);
49
+ }
50
+ :host(:state(attr-orientation-is-horizontal)) {
51
+ grid-auto-flow: column;
52
+ justify-content: start;
53
+ }
54
+ label {
55
+ display: inline-flex;
56
+ align-items: center;
57
+ gap: calc(var(--spacing) * 2);
58
+ font-size: var(--text-sm);
59
+ font-weight: var(--font-weight-medium);
60
+ cursor: pointer;
61
+ }
62
+ input {
63
+ appearance: none;
64
+ display: grid;
65
+ place-content: center;
66
+ width: calc(var(--spacing) * 4);
67
+ height: calc(var(--spacing) * 4);
68
+ margin: 0;
69
+ border: 1px solid var(--input);
70
+ border-radius: var(--radius-full);
71
+ box-shadow: var(--shadow-xs);
72
+ cursor: pointer;
73
+ outline: none;
74
+ }
75
+ input::before {
76
+ content: "";
77
+ width: calc(var(--spacing) * 2);
78
+ height: calc(var(--spacing) * 2);
79
+ border-radius: var(--radius-full);
80
+ background: var(--primary);
81
+ transform: scale(0);
82
+ transition: transform 0.1s;
83
+ }
84
+ input:checked::before {
85
+ transform: scale(1);
86
+ }
87
+ input:focus-visible {
88
+ border-color: var(--ring);
89
+ box-shadow: var(--focus-ring);
90
+ }
91
+ input:disabled,
92
+ input:disabled + span {
93
+ cursor: not-allowed;
94
+ opacity: 0.5;
95
+ }
96
+ .list {
97
+ display: contents;
98
+ }
99
+ .options {
100
+ display: none;
101
+ }
102
+ </style>
103
+
104
+ <div class="list" ref="list">
105
+ <repeat-list of="#group.options" as="option" key="id">
106
+ <label part="item">
107
+ <input
108
+ type="radio"
109
+ name="group"
110
+ part="control"
111
+ _value="#option.value"
112
+ _checked="#option.checked"
113
+ _disabled="#option.disabled"
114
+ onchange="choose(option)">
115
+ <span><slot name="#option.label"></slot></span>
116
+ </label>
117
+ </repeat-list>
118
+ </div>
119
+ <div class="options"><slot></slot></div>
120
+ </custom-element>
@@ -0,0 +1,170 @@
1
+ <custom-element name="ui-resizable">
2
+ <script>
3
+ defineProps({
4
+ direction: prop.enum(["horizontal", "vertical"]).default("horizontal"),
5
+ variant: prop.enum(["default", "outline"]).default("default"),
6
+ });
7
+ </script>
8
+
9
+ <style>
10
+ /* A double tap must not zoom the page. Set inside the shadow root, because a page-level
11
+ touch-action does not reach shadow content in iOS Safari. */
12
+ :host,
13
+ * {
14
+ touch-action: manipulation;
15
+ }
16
+ :host {
17
+ display: flex;
18
+ width: 100%;
19
+ height: 100%;
20
+ overflow: hidden;
21
+ }
22
+ :host(:state(attr-height)) {
23
+ height: calc(var(--spacing) * var(--attr-height));
24
+ }
25
+ :host(:state(attr-variant-is-outline)) {
26
+ box-sizing: border-box;
27
+ border: 1px solid var(--border);
28
+ border-radius: var(--radius-lg);
29
+ }
30
+ :host(:state(attr-direction-is-vertical)) {
31
+ flex-direction: column;
32
+ }
33
+ </style>
34
+
35
+ <slot></slot>
36
+ </custom-element>
37
+
38
+ <custom-element name="ui-resizable-panel">
39
+ <script>
40
+ defineProps({ size: prop.number().min(0).max(100).default(50), min: prop.number().min(0).default(10) });
41
+ </script>
42
+
43
+ <style>
44
+ :host,
45
+ * {
46
+ touch-action: manipulation;
47
+ }
48
+ :host {
49
+ display: block;
50
+ flex: var(--attr-size) 1 0;
51
+ min-width: 0;
52
+ min-height: 0;
53
+ overflow: auto;
54
+ }
55
+ </style>
56
+
57
+ <slot></slot>
58
+ </custom-element>
59
+
60
+ <custom-element name="ui-resizable-handle" role="separator">
61
+ <script>
62
+ const props = defineProps({ handle: prop.boolean() });
63
+
64
+ const drag = useState({ active: false });
65
+ const KEYS = { ArrowLeft: -5, ArrowUp: -5, ArrowRight: 5, ArrowDown: 5 };
66
+
67
+ function vertical() {
68
+ return host.parentElement?.getAttribute("direction") === "vertical";
69
+ }
70
+
71
+ function resize(delta) {
72
+ const before = host.previousElementSibling;
73
+ const after = host.nextElementSibling;
74
+ if (!before || !after) return;
75
+ const total = before.size + after.size;
76
+ const size = Math.min(Math.max(before.size + delta, before.min), total - after.min);
77
+ before.size = size;
78
+ after.size = total - size;
79
+ internals.ariaValueNow = String(Math.round(size));
80
+ }
81
+
82
+ function start(event) {
83
+ drag.active = true;
84
+ if (event.isTrusted) host.setPointerCapture(event.pointerId);
85
+ }
86
+
87
+ function move(event) {
88
+ if (!drag.active) return;
89
+ const parent = host.parentElement.getBoundingClientRect();
90
+ const length = vertical() ? parent.height : parent.width;
91
+ const pixels = vertical() ? event.movementY : event.movementX;
92
+ const before = host.previousElementSibling;
93
+ const after = host.nextElementSibling;
94
+ resize(((before.size + after.size) * pixels) / length);
95
+ }
96
+
97
+ function stop() {
98
+ drag.active = false;
99
+ }
100
+
101
+ function keys(event) {
102
+ const step = KEYS[event.key];
103
+ if (!step) return;
104
+ event.preventDefault();
105
+ resize(step);
106
+ }
107
+
108
+ attrs.tabindex ??= 0;
109
+ listen("pointerdown", start);
110
+ listen("pointermove", move);
111
+ listen("pointerup", stop);
112
+ listen("keydown", keys);
113
+
114
+ onConnected(() => {
115
+ if (vertical()) internals.states.add("vertical");
116
+ internals.ariaOrientation = vertical() ? "horizontal" : "vertical";
117
+ internals.ariaValueMin = "0";
118
+ internals.ariaValueMax = "100";
119
+ });
120
+ </script>
121
+
122
+ <style>
123
+ :host,
124
+ * {
125
+ touch-action: manipulation;
126
+ }
127
+ :host {
128
+ position: relative;
129
+ display: flex;
130
+ flex-shrink: 0;
131
+ align-items: center;
132
+ justify-content: center;
133
+ width: 1px;
134
+ background: var(--border);
135
+ cursor: col-resize;
136
+ outline: none;
137
+ touch-action: none;
138
+ }
139
+ :host(:state(vertical)) {
140
+ width: auto;
141
+ height: 1px;
142
+ cursor: row-resize;
143
+ }
144
+ :host(:state(vertical))::after {
145
+ inset: calc(var(--spacing) * -1) 0;
146
+ }
147
+ :host::after {
148
+ content: "";
149
+ position: absolute;
150
+ inset: 0 calc(var(--spacing) * -1);
151
+ }
152
+ :host(:focus-visible) {
153
+ box-shadow: 0 0 0 1px var(--ring);
154
+ }
155
+ .grip {
156
+ display: none;
157
+ z-index: 1;
158
+ width: calc(var(--spacing) * 3);
159
+ height: calc(var(--spacing) * 4);
160
+ border: 1px solid var(--border);
161
+ border-radius: var(--radius-xs);
162
+ background: var(--border);
163
+ }
164
+ :host(:state(attr-handle)) .grip {
165
+ display: block;
166
+ }
167
+ </style>
168
+
169
+ <div class="grip" part="grip"></div>
170
+ </custom-element>