@relements/core 0.1.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/dist/base.css +1 -0
- package/dist/behaviors/dialog.d.ts +39 -0
- package/dist/behaviors/dialog.js +1 -0
- package/dist/behaviors/dismissible.d.ts +37 -0
- package/dist/behaviors/dismissible.js +1 -0
- package/dist/behaviors/menu-button.d.ts +36 -0
- package/dist/behaviors/menu-button.js +1 -0
- package/dist/behaviors/popover.d.ts +28 -0
- package/dist/behaviors/popover.js +1 -0
- package/dist/behaviors/tabs.d.ts +37 -0
- package/dist/behaviors/tabs.js +1 -0
- package/dist/behaviors/toast.d.ts +42 -0
- package/dist/behaviors/toast.js +1 -0
- package/dist/chunk-GMICGIQW.js +149 -0
- package/dist/chunk-J4EGUBPP.js +68 -0
- package/dist/chunk-PIDPGDBZ.js +62 -0
- package/dist/chunk-PSODVT3V.js +67 -0
- package/dist/chunk-TC4TFP7Y.js +40 -0
- package/dist/chunk-ZHRJNWMH.js +174 -0
- package/dist/components/button.css +1 -0
- package/dist/components/dialog.css +1 -0
- package/dist/components/disclosure.css +1 -0
- package/dist/components/form.css +1 -0
- package/dist/components/link.css +1 -0
- package/dist/components/menu.css +1 -0
- package/dist/components/popover.css +1 -0
- package/dist/components/progress.css +1 -0
- package/dist/components/tabs.css +1 -0
- package/dist/components/toast.css +1 -0
- package/dist/elements/re-menu.d.ts +10 -0
- package/dist/elements/re-menu.js +36 -0
- package/dist/elements/re-popover.d.ts +12 -0
- package/dist/elements/re-popover.js +35 -0
- package/dist/elements/re-tabs.d.ts +20 -0
- package/dist/elements/re-tabs.js +60 -0
- package/dist/elements/re-toast.d.ts +15 -0
- package/dist/elements/re-toast.js +30 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/reset.css +1 -0
- package/dist/themes/renascent.css +1 -0
- package/dist/tokens.css +1 -0
- package/package.json +84 -0
- package/src/base.css +129 -0
- package/src/behaviors/dialog.js +106 -0
- package/src/behaviors/dismissible.js +68 -0
- package/src/behaviors/menu-button.js +199 -0
- package/src/behaviors/popover.js +103 -0
- package/src/behaviors/tabs.js +171 -0
- package/src/behaviors/toast.js +97 -0
- package/src/components/button.css +141 -0
- package/src/components/dialog.css +106 -0
- package/src/components/disclosure.css +83 -0
- package/src/components/form.css +334 -0
- package/src/components/link.css +61 -0
- package/src/components/menu.css +78 -0
- package/src/components/popover.css +50 -0
- package/src/components/progress.css +112 -0
- package/src/components/tabs.css +86 -0
- package/src/components/toast.css +87 -0
- package/src/elements/re-menu.js +54 -0
- package/src/elements/re-popover.js +59 -0
- package/src/elements/re-tabs.js +92 -0
- package/src/elements/re-toast.js +46 -0
- package/src/index.css +30 -0
- package/src/index.js +13 -0
- package/src/reset.css +103 -0
- package/src/themes/renascent.css +198 -0
- package/src/tokens.css +196 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relements tabs.
|
|
3
|
+
*
|
|
4
|
+
* Visual layer for the ARIA tab pattern. The DOM contract:
|
|
5
|
+
*
|
|
6
|
+
* <div class="re-tabs" data-re-tabs>
|
|
7
|
+
* <div class="re-tabs__list" role="tablist" aria-label="…">
|
|
8
|
+
* <button class="re-tab" role="tab" id="t-1" aria-controls="p-1" aria-selected="true">Profile</button>
|
|
9
|
+
* <button class="re-tab" role="tab" id="t-2" aria-controls="p-2" aria-selected="false" tabindex="-1">Security</button>
|
|
10
|
+
* </div>
|
|
11
|
+
* <section class="re-tabpanel" role="tabpanel" id="p-1" aria-labelledby="t-1">…</section>
|
|
12
|
+
* <section class="re-tabpanel" role="tabpanel" id="p-2" aria-labelledby="t-2" hidden>…</section>
|
|
13
|
+
* </div>
|
|
14
|
+
*
|
|
15
|
+
* Server-rendered initial state shows the selected tab; enhanceTabs adds
|
|
16
|
+
* keyboard navigation and runtime selection.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
@layer re.components {
|
|
20
|
+
.re-tabs {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
gap: var(--re-space-4);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.re-tabs__list {
|
|
27
|
+
display: flex;
|
|
28
|
+
gap: var(--re-space-1);
|
|
29
|
+
flex-wrap: wrap;
|
|
30
|
+
border-block-end: var(--re-border-default);
|
|
31
|
+
padding-block-end: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.re-tab {
|
|
35
|
+
appearance: none;
|
|
36
|
+
background: transparent;
|
|
37
|
+
border: 0;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
font: inherit;
|
|
40
|
+
color: var(--re-color-text-muted);
|
|
41
|
+
padding: var(--re-space-2) var(--re-space-4);
|
|
42
|
+
border-radius: var(--re-radius-md) var(--re-radius-md) 0 0;
|
|
43
|
+
position: relative;
|
|
44
|
+
line-height: 1.4;
|
|
45
|
+
font-weight: var(--re-font-weight-medium);
|
|
46
|
+
transition:
|
|
47
|
+
color var(--re-duration-fast) var(--re-easing-standard),
|
|
48
|
+
background-color var(--re-duration-fast) var(--re-easing-standard);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.re-tab:hover {
|
|
52
|
+
color: var(--re-color-text);
|
|
53
|
+
background-color: var(--re-color-bg-subtle);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.re-tab:focus-visible {
|
|
57
|
+
outline: none;
|
|
58
|
+
box-shadow: var(--re-shadow-focus);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.re-tab[aria-selected="true"] {
|
|
62
|
+
color: var(--re-color-text);
|
|
63
|
+
background-color: var(--re-color-bg);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Active underline indicator */
|
|
67
|
+
.re-tab[aria-selected="true"]::after {
|
|
68
|
+
content: "";
|
|
69
|
+
position: absolute;
|
|
70
|
+
inset-inline: var(--re-space-2);
|
|
71
|
+
inset-block-end: calc(var(--re-border-width) * -1);
|
|
72
|
+
block-size: 2px;
|
|
73
|
+
background-color: var(--re-color-accent-600);
|
|
74
|
+
border-radius: var(--re-radius-sm);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.re-tabpanel {
|
|
78
|
+
color: var(--re-color-text);
|
|
79
|
+
line-height: var(--re-line-height-normal);
|
|
80
|
+
}
|
|
81
|
+
.re-tabpanel:focus-visible {
|
|
82
|
+
outline: none;
|
|
83
|
+
box-shadow: var(--re-shadow-focus);
|
|
84
|
+
border-radius: var(--re-radius-md);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relements toast.
|
|
3
|
+
*
|
|
4
|
+
* <div class="re-toast-region" role="region" aria-label="Notifications" data-re-toast-region>
|
|
5
|
+
* <div class="re-toast-list" aria-live="polite" aria-relevant="additions"></div>
|
|
6
|
+
* </div>
|
|
7
|
+
*
|
|
8
|
+
* showToast(message, { tone, duration }) appends a <div class="re-toast"> item
|
|
9
|
+
* with the appropriate tone. Items are removed on timeout or dismiss.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
@layer re.components {
|
|
13
|
+
.re-toast-region {
|
|
14
|
+
position: fixed;
|
|
15
|
+
inset-block-end: var(--re-space-6);
|
|
16
|
+
inset-inline-end: var(--re-space-6);
|
|
17
|
+
z-index: var(--re-z-toast);
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.re-toast-list {
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: column-reverse;
|
|
24
|
+
gap: var(--re-space-2);
|
|
25
|
+
margin: 0;
|
|
26
|
+
padding: 0;
|
|
27
|
+
min-inline-size: 16rem;
|
|
28
|
+
max-inline-size: 24rem;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.re-toast {
|
|
32
|
+
pointer-events: auto;
|
|
33
|
+
display: flex;
|
|
34
|
+
gap: var(--re-space-3);
|
|
35
|
+
align-items: flex-start;
|
|
36
|
+
padding: var(--re-space-3) var(--re-space-4);
|
|
37
|
+
background-color: var(--re-color-surface);
|
|
38
|
+
color: var(--re-color-text);
|
|
39
|
+
border: var(--re-border-default);
|
|
40
|
+
border-radius: var(--re-radius-md);
|
|
41
|
+
box-shadow: var(--re-shadow-md);
|
|
42
|
+
line-height: var(--re-line-height-normal);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.re-toast__body {
|
|
46
|
+
flex: 1 1 auto;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.re-toast__dismiss {
|
|
50
|
+
appearance: none;
|
|
51
|
+
background: transparent;
|
|
52
|
+
border: 0;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
color: var(--re-color-text-muted);
|
|
55
|
+
font: inherit;
|
|
56
|
+
font-size: var(--re-size-text-lg);
|
|
57
|
+
line-height: 1;
|
|
58
|
+
padding: 0 var(--re-space-1);
|
|
59
|
+
border-radius: var(--re-radius-sm);
|
|
60
|
+
}
|
|
61
|
+
.re-toast__dismiss:hover {
|
|
62
|
+
color: var(--re-color-text);
|
|
63
|
+
background: var(--re-color-bg-subtle);
|
|
64
|
+
}
|
|
65
|
+
.re-toast__dismiss:focus-visible {
|
|
66
|
+
outline: none;
|
|
67
|
+
box-shadow: var(--re-shadow-focus);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Tone variants: tinted background + colored border + colored label.
|
|
71
|
+
color-mix() adapts to both light and dark surface colours. */
|
|
72
|
+
.re-toast[data-tone="success"] {
|
|
73
|
+
background-color: color-mix(in srgb, var(--re-color-success-600) 12%, var(--re-color-surface));
|
|
74
|
+
border-color: var(--re-color-success-600);
|
|
75
|
+
color: var(--re-color-text);
|
|
76
|
+
}
|
|
77
|
+
.re-toast[data-tone="danger"] {
|
|
78
|
+
background-color: color-mix(in srgb, var(--re-color-danger-600) 12%, var(--re-color-surface));
|
|
79
|
+
border-color: var(--re-color-danger-600);
|
|
80
|
+
color: var(--re-color-text);
|
|
81
|
+
}
|
|
82
|
+
.re-toast[data-tone="warning"] {
|
|
83
|
+
background-color: color-mix(in srgb, var(--re-color-warning-600) 12%, var(--re-color-surface));
|
|
84
|
+
border-color: var(--re-color-warning-600);
|
|
85
|
+
color: var(--re-color-text);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <re-menu>
|
|
3
|
+
* ---------
|
|
4
|
+
* Custom element wrapping the menu-button pattern over light-DOM markup.
|
|
5
|
+
*
|
|
6
|
+
* <re-menu>
|
|
7
|
+
* <button class="re-button" aria-haspopup="menu" aria-expanded="false" aria-controls="m" id="b">Actions</button>
|
|
8
|
+
* <div class="re-menu__panel" role="menu" id="m" aria-labelledby="b" hidden>
|
|
9
|
+
* <button class="re-menu__item" role="menuitem" data-value="a">A</button>
|
|
10
|
+
* </div>
|
|
11
|
+
* </re-menu>
|
|
12
|
+
*
|
|
13
|
+
* The element exposes:
|
|
14
|
+
* - `.open` boolean reflecting/setting the open state.
|
|
15
|
+
* - bubbles `re-select` from the underlying enhancer.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { enhanceMenuButton } from "../behaviors/menu-button.js";
|
|
19
|
+
|
|
20
|
+
export class ReMenuElement extends HTMLElement {
|
|
21
|
+
/** @type {{ destroy: () => void } | null} */
|
|
22
|
+
#controller = null;
|
|
23
|
+
|
|
24
|
+
connectedCallback() {
|
|
25
|
+
this.classList.add("re-menu");
|
|
26
|
+
this.setAttribute("data-re-menu", "");
|
|
27
|
+
this.#controller = enhanceMenuButton(this);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
disconnectedCallback() {
|
|
31
|
+
this.#controller?.destroy();
|
|
32
|
+
this.#controller = null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** @returns {boolean} */
|
|
36
|
+
get open() {
|
|
37
|
+
const btn = this.querySelector('[aria-haspopup="menu"], [aria-haspopup="true"]');
|
|
38
|
+
return btn?.getAttribute("aria-expanded") === "true";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
set open(value) {
|
|
42
|
+
const btn = /** @type {HTMLElement | null} */ (
|
|
43
|
+
this.querySelector('[aria-haspopup="menu"], [aria-haspopup="true"]')
|
|
44
|
+
);
|
|
45
|
+
if (!btn) return;
|
|
46
|
+
const want = Boolean(value);
|
|
47
|
+
if (this.open === want) return;
|
|
48
|
+
btn.click();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (typeof customElements !== "undefined" && !customElements.get("re-menu")) {
|
|
53
|
+
customElements.define("re-menu", ReMenuElement);
|
|
54
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <re-popover>
|
|
3
|
+
* ------------
|
|
4
|
+
* Custom element wrapping the native popover attribute.
|
|
5
|
+
*
|
|
6
|
+
* <button class="re-button" popovertarget="tip">Toggle</button>
|
|
7
|
+
* <re-popover id="tip">
|
|
8
|
+
* <p>Hello!</p>
|
|
9
|
+
* </re-popover>
|
|
10
|
+
*
|
|
11
|
+
* The element receives the native `popover` attribute on connect and
|
|
12
|
+
* runs enhancePopover for anchored positioning + re-toggle event.
|
|
13
|
+
*
|
|
14
|
+
* Methods:
|
|
15
|
+
* show() — calls showPopover()
|
|
16
|
+
* hide() — calls hidePopover()
|
|
17
|
+
* toggle() — calls togglePopover()
|
|
18
|
+
*
|
|
19
|
+
* Property:
|
|
20
|
+
* open — boolean reflecting native :popover-open state
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { enhancePopover } from "../behaviors/popover.js";
|
|
24
|
+
|
|
25
|
+
export class RePopoverElement extends HTMLElement {
|
|
26
|
+
/** @type {{ destroy: () => void } | null} */
|
|
27
|
+
#controller = null;
|
|
28
|
+
|
|
29
|
+
connectedCallback() {
|
|
30
|
+
if (!this.hasAttribute("popover")) this.setAttribute("popover", "auto");
|
|
31
|
+
this.classList.add("re-popover");
|
|
32
|
+
this.setAttribute("data-re-popover", "");
|
|
33
|
+
this.#controller = enhancePopover(this);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
disconnectedCallback() {
|
|
37
|
+
this.#controller?.destroy();
|
|
38
|
+
this.#controller = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** @returns {boolean} */
|
|
42
|
+
get open() {
|
|
43
|
+
return this.matches?.(":popover-open") ?? false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
show() {
|
|
47
|
+
/** @type {any} */ (this).showPopover?.();
|
|
48
|
+
}
|
|
49
|
+
hide() {
|
|
50
|
+
/** @type {any} */ (this).hidePopover?.();
|
|
51
|
+
}
|
|
52
|
+
toggle() {
|
|
53
|
+
/** @type {any} */ (this).togglePopover?.();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof customElements !== "undefined" && !customElements.get("re-popover")) {
|
|
58
|
+
customElements.define("re-popover", RePopoverElement);
|
|
59
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <re-tabs>
|
|
3
|
+
* ---------
|
|
4
|
+
* Custom-element wrapper around enhanceTabs over light-DOM markup.
|
|
5
|
+
*
|
|
6
|
+
* <re-tabs aria-label="Account">
|
|
7
|
+
* <div role="tablist">
|
|
8
|
+
* <button role="tab" id="t-1" aria-controls="p-1" aria-selected="true">Profile</button>
|
|
9
|
+
* <button role="tab" id="t-2" aria-controls="p-2" aria-selected="false" tabindex="-1">Security</button>
|
|
10
|
+
* </div>
|
|
11
|
+
* <section role="tabpanel" id="p-1" aria-labelledby="t-1">…</section>
|
|
12
|
+
* <section role="tabpanel" id="p-2" aria-labelledby="t-2" hidden>…</section>
|
|
13
|
+
* </re-tabs>
|
|
14
|
+
*
|
|
15
|
+
* Exposes:
|
|
16
|
+
* - Property `value`: id of the selected tab (e.g. "t-1"). Set to switch.
|
|
17
|
+
* - Re-dispatches `re-change` from the underlying enhancer with bubbles.
|
|
18
|
+
* - Works framework-neutrally: any HTML rendering produces the same contract.
|
|
19
|
+
*
|
|
20
|
+
* import "@relements/core/elements/re-tabs";
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { enhanceTabs } from "../behaviors/tabs.js";
|
|
24
|
+
|
|
25
|
+
export class ReTabsElement extends HTMLElement {
|
|
26
|
+
/** @type {{ destroy: () => void } | null} */
|
|
27
|
+
#controller = null;
|
|
28
|
+
|
|
29
|
+
static get observedAttributes() {
|
|
30
|
+
return ["value"];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
connectedCallback() {
|
|
34
|
+
// Mark the host so enhanceTabs picks it up.
|
|
35
|
+
this.setAttribute("data-re-tabs", "");
|
|
36
|
+
this.#controller = enhanceTabs(this);
|
|
37
|
+
// Honor a value attribute if present and different from initial state.
|
|
38
|
+
const initial = this.getAttribute("value");
|
|
39
|
+
if (initial) this.#selectById(initial);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
disconnectedCallback() {
|
|
43
|
+
this.#controller?.destroy();
|
|
44
|
+
this.#controller = null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {string} name
|
|
49
|
+
* @param {string | null} _oldValue
|
|
50
|
+
* @param {string | null} newValue
|
|
51
|
+
*/
|
|
52
|
+
attributeChangedCallback(name, _oldValue, newValue) {
|
|
53
|
+
if (name === "value" && newValue) {
|
|
54
|
+
this.#selectById(newValue);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Currently selected tab id.
|
|
60
|
+
* @returns {string | null}
|
|
61
|
+
*/
|
|
62
|
+
get value() {
|
|
63
|
+
const sel = this.querySelector('[role="tab"][aria-selected="true"]');
|
|
64
|
+
return sel ? sel.id : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
set value(id) {
|
|
68
|
+
if (!id) return;
|
|
69
|
+
this.setAttribute("value", id);
|
|
70
|
+
this.#selectById(id);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {string} id
|
|
75
|
+
*/
|
|
76
|
+
#selectById(id) {
|
|
77
|
+
/** @type {HTMLElement | null} */
|
|
78
|
+
const tab = this.querySelector(`[role="tab"]#${cssEscape(id)}`);
|
|
79
|
+
if (tab && tab.getAttribute("aria-selected") !== "true") {
|
|
80
|
+
tab.click();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** @param {string} value */
|
|
86
|
+
function cssEscape(value) {
|
|
87
|
+
return typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (typeof customElements !== "undefined" && !customElements.get("re-tabs")) {
|
|
91
|
+
customElements.define("re-tabs", ReTabsElement);
|
|
92
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <re-toast>
|
|
3
|
+
* ----------
|
|
4
|
+
* Custom element that hosts a toast region in light DOM and exposes a
|
|
5
|
+
* `.show(message, options)` method matching the showToast() API.
|
|
6
|
+
*
|
|
7
|
+
* <re-toast id="toaster"></re-toast>
|
|
8
|
+
* <script type="module">
|
|
9
|
+
* import "@relements/core/elements/re-toast";
|
|
10
|
+
* document.getElementById("toaster").show("Hi", { tone: "success" });
|
|
11
|
+
* </script>
|
|
12
|
+
*
|
|
13
|
+
* On connection the element materializes a `.re-toast-region` / `.re-toast-list`
|
|
14
|
+
* inside itself if not already present. Toasts use the same CSS as the
|
|
15
|
+
* functional API.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { showToast } from "../behaviors/toast.js";
|
|
19
|
+
|
|
20
|
+
export class ReToastElement extends HTMLElement {
|
|
21
|
+
connectedCallback() {
|
|
22
|
+
if (!this.querySelector("[data-re-toast-region]")) {
|
|
23
|
+
this.classList.add("re-toast-region");
|
|
24
|
+
this.setAttribute("role", "region");
|
|
25
|
+
if (!this.hasAttribute("aria-label")) this.setAttribute("aria-label", "Notifications");
|
|
26
|
+
this.setAttribute("data-re-toast-region", "");
|
|
27
|
+
const list = document.createElement("div");
|
|
28
|
+
list.className = "re-toast-list";
|
|
29
|
+
list.setAttribute("aria-live", this.getAttribute("data-live") ?? "polite");
|
|
30
|
+
list.setAttribute("aria-relevant", "additions");
|
|
31
|
+
this.appendChild(list);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {string} message
|
|
37
|
+
* @param {Parameters<typeof showToast>[1]} [options]
|
|
38
|
+
*/
|
|
39
|
+
show(message, options) {
|
|
40
|
+
return showToast(message, { ...options, root: this });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof customElements !== "undefined" && !customElements.get("re-toast")) {
|
|
45
|
+
customElements.define("re-toast", ReToastElement);
|
|
46
|
+
}
|
package/src/index.css
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relements core entry stylesheet.
|
|
3
|
+
*
|
|
4
|
+
* Cascade layer order (lowest → highest specificity-wins):
|
|
5
|
+
* 1. re.tokens — design tokens
|
|
6
|
+
* 2. re.reset — minimal cross-browser normalization
|
|
7
|
+
* 3. re.base — document-level typography & native element defaults
|
|
8
|
+
* 4. re.components — opt-in `.re-*` component styles
|
|
9
|
+
*
|
|
10
|
+
* Consumer overrides land in the unlayered cascade and win automatically.
|
|
11
|
+
* Subtree theming: redeclare tokens on any ancestor element.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
@layer re.tokens, re.reset, re.base, re.components;
|
|
15
|
+
|
|
16
|
+
@import "./tokens.css" layer(re.tokens);
|
|
17
|
+
@import "./reset.css" layer(re.reset);
|
|
18
|
+
@import "./base.css" layer(re.base);
|
|
19
|
+
|
|
20
|
+
/* Components */
|
|
21
|
+
@import "./components/button.css";
|
|
22
|
+
@import "./components/link.css";
|
|
23
|
+
@import "./components/form.css";
|
|
24
|
+
@import "./components/disclosure.css";
|
|
25
|
+
@import "./components/dialog.css";
|
|
26
|
+
@import "./components/progress.css";
|
|
27
|
+
@import "./components/tabs.css";
|
|
28
|
+
@import "./components/menu.css";
|
|
29
|
+
@import "./components/popover.css";
|
|
30
|
+
@import "./components/toast.css";
|
package/src/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @relements/core entry point.
|
|
3
|
+
*
|
|
4
|
+
* Behaviors are tree-shakable named exports. CSS is imported separately
|
|
5
|
+
* via the package's exports map (e.g. `@relements/core/index.css`).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { enhanceDismissible } from "./behaviors/dismissible.js";
|
|
9
|
+
export { enhanceDialog } from "./behaviors/dialog.js";
|
|
10
|
+
export { enhanceTabs } from "./behaviors/tabs.js";
|
|
11
|
+
export { enhanceMenuButton } from "./behaviors/menu-button.js";
|
|
12
|
+
export { enhancePopover } from "./behaviors/popover.js";
|
|
13
|
+
export { showToast } from "./behaviors/toast.js";
|
package/src/reset.css
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relements reset.
|
|
3
|
+
*
|
|
4
|
+
* A minimal, opinionated normalization. Keeps native semantics intact;
|
|
5
|
+
* only removes obvious cross-browser inconsistencies and unifies box sizing.
|
|
6
|
+
* Native form controls retain native behavior — components opt in via classes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
@layer re.reset {
|
|
10
|
+
*,
|
|
11
|
+
*::before,
|
|
12
|
+
*::after {
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
html {
|
|
17
|
+
-webkit-text-size-adjust: 100%;
|
|
18
|
+
text-size-adjust: 100%;
|
|
19
|
+
tab-size: 4;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
body {
|
|
23
|
+
margin: 0;
|
|
24
|
+
min-height: 100vh;
|
|
25
|
+
min-height: 100dvh;
|
|
26
|
+
line-height: var(--re-line-height-normal);
|
|
27
|
+
-webkit-font-smoothing: antialiased;
|
|
28
|
+
-moz-osx-font-smoothing: grayscale;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Reset margins on common block elements; opt in via base.css. */
|
|
32
|
+
h1,
|
|
33
|
+
h2,
|
|
34
|
+
h3,
|
|
35
|
+
h4,
|
|
36
|
+
h5,
|
|
37
|
+
h6,
|
|
38
|
+
p,
|
|
39
|
+
figure,
|
|
40
|
+
blockquote,
|
|
41
|
+
dl,
|
|
42
|
+
dd {
|
|
43
|
+
margin: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
ul[role="list"],
|
|
47
|
+
ol[role="list"] {
|
|
48
|
+
list-style: none;
|
|
49
|
+
margin: 0;
|
|
50
|
+
padding: 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
img,
|
|
54
|
+
picture,
|
|
55
|
+
video,
|
|
56
|
+
canvas,
|
|
57
|
+
svg {
|
|
58
|
+
display: block;
|
|
59
|
+
max-width: 100%;
|
|
60
|
+
height: auto;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Inherit fonts in form controls — natural for typography tokens to apply. */
|
|
64
|
+
input,
|
|
65
|
+
button,
|
|
66
|
+
textarea,
|
|
67
|
+
select {
|
|
68
|
+
font: inherit;
|
|
69
|
+
color: inherit;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
textarea {
|
|
73
|
+
resize: vertical;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Avoid orphaned text-flow problems. */
|
|
77
|
+
p,
|
|
78
|
+
h1,
|
|
79
|
+
h2,
|
|
80
|
+
h3,
|
|
81
|
+
h4,
|
|
82
|
+
h5,
|
|
83
|
+
h6 {
|
|
84
|
+
overflow-wrap: break-word;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Honor the [hidden] attribute even when component CSS sets display. */
|
|
88
|
+
[hidden] {
|
|
89
|
+
display: none !important;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Respect users who request reduced motion. */
|
|
93
|
+
@media (prefers-reduced-motion: reduce) {
|
|
94
|
+
*,
|
|
95
|
+
*::before,
|
|
96
|
+
*::after {
|
|
97
|
+
animation-duration: 0.01ms !important;
|
|
98
|
+
animation-iteration-count: 1 !important;
|
|
99
|
+
transition-duration: 0.01ms !important;
|
|
100
|
+
scroll-behavior: auto !important;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|