components
All repositories: gitoria
4.5 KB
# ModalA window over the page with a title, a × and any content; the page behind is dimmed, blocked and does not scroll.Esc, the × and a click on the dim background close it (the background click can be switched off); buttons in a footerclose it too. The focus stays inside while it is open and goes back to the element that opened it. The page opens andcloses it and is told **how** it was closed. Demo: `/modal` (https://components.hybriel.worldapi.org/modal).Pure Hybriel: one file, `modal.hl`, with its own `Style`; no JavaScript file. It is the browser's own `<dialog>` openedwith `showModal()` (page behind inert, focus in, focus back), plus Tab / Shift+Tab going round inside, the reasons, thebackground click and the scroll lock.## Use it in an appCopy `components/modal/modal.hl` (optionally `shared/tokens.hl` for the app's own accent), then:```import Modal from './modal.hl'String result = ''View {page { on close(e) { emit pageClosed(e) }button { type = "button" "Delete…" on click(e) { emit pageAsk(e) } }Modal { id = "confirm" title = "Delete the draft?"p { "This cannot be undone." }footer {button { type = "button" value = "cancel" "Cancel" }button { type = "button" value = "ok" "Delete" }}}}}on pageAsk(e) { document.getElementById('confirm').showModal() }on pageClosed(e) { if (e.detail.id == 'confirm' && e.detail.reason == 'ok') { result = 'deleted' } }```## Attributes| Attribute | Meaning ||---|---|| `id` | the id of the `<dialog>`: the page opens / closes it by this id (required when there are several) — default `modal` || `title` | the heading of the window (also its `aria-label`) || `backdropClose` | `"false"` = a click on the dim background does NOT close it (default `"true"`) || `closeLabel` | the tooltip / label of the × (default `Close`) || content | everything written inside `Modal { … }` is the content (the host's content: its handlers and members are the page's) |Write every attribute as a **literal** (`title = "…"`): then several modals stand on one page, each with its own values.(A composed component's members are the page's state; bound to a changing member, all modals would share it.)## Footer buttonsA `footer { … }` inside the content is the footer: laid out at the bottom right, sticky under a long content, the lastbutton is the main one (accent colour; black without a theme). A click on a footer button closes the modal with the button's `value` as thereason (no `value` → its text). A footer button may have its own `on click` (the page's handler): it runs first, and whenit closes the modal itself with `close('saved')`, that reason wins (the demo's "Save"). A button that must keep the modalopen (e.g. a check that fails) goes in the content, not in the `footer`.## Open and close from the page- open: `document.getElementById('<id>').showModal()` in any handler (the focused element — usually the button that wasclicked — gets the focus back when it closes).- close: `document.getElementById('<id>').close('<reason>')` — the reason is what the page is told.## EventsWhen it closes, a **bubbling `close` event** is sent from `<modal-dialog>`; catch it on any element around the modal:`on close(e) { … }`.| `e.detail.reason` | how it was closed ||---|---|| `close` | the × || `escape` | the Esc key || `backdrop` | a click on the dim background || the button's `value` (or text) | a footer button || whatever the page passed | `close('<reason>')` from the page |`e.detail.id` is the modal's id. (A custom event name such as `modalclose` would be nicer, but hl:web binds onlystandard DOM event names — hybriel ticket #31 — so it is `close`.)## Behaviour- The page behind is inert (no clicks, no focus) and does not scroll (`html { overflow: hidden }` while one is open).- The window stays inside the screen (at most the screen height minus 2rem); a long content scrolls inside it.- A press that starts inside the window and ends on the background (selecting text) does not close it.- Phone: the window is the screen width minus 1rem each side.- Colours are the semantic tokens with a black/white/grey fallback (the base look is black, white and greys (no frames; root README "Look"); an app that declares the WorldAPI tokens (layouts.worldapi.org's theme) restyles it): a white window on the grey dimmed page; buttons have no border.## Not yetOpen / close animation; stacking one modal on top of another is untested; the page cannot bind the modal's `open` stateas a member (it opens it with `showModal()`).
Branches
- mainmain branch
Latest commits
- 3bc905c5components #15 (mission 003) 2/2: docs (README Look, STATUS, LOG), reportmre
- 5a98d77ecomponents #15 (mission 003) 1/2: black/white base look — greys for nuances, no WorldAPI fallbacks, no decorative frames; site + icon black/white; gate 230/0mre
- 3f93d716components code order (mission 002) 4/4: docs, tests/letcount.py, tests/same-output*, reportmre
- e747cfa0components code order (mission 002) 3/4: let only where reassigned (257 dropped, 0 never-reassigned left); gate 220/0, layouts gate 73/0, same outputmre
- 172914a3components code order (mission 002) 2/4: lib/uploads.hl (demo upload storage), thin demo handlers, project.hl map; gate 220/0, same outputmre
- d29f35b1components code order (mission 002) 1/4: styles.hl -> components/styles.hl; gate 220/0, same outputmre
- 09945750components: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, http1 773de63e); gate 220/0mre
- ede4d19acomponents: Hybriel master 190aa11d (fc838894 GC correctness, #126 closure scopes, #127); gate 220/0mre
- 2bb9a935components: Hybriel master 8efba065 (re-vendor round 069: #126 memory, #48 lambda copy)mre
- e4f01768antcolony#40: mission references point to the moved missionsmre
- d32b44e2antcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
- 0fb80b57components: Hybriel master ff51cf46 (re-vendor round)mre
- 57408b6ecomponents#14: installable app (manifest, service worker, offline index), own iconmre
- 317bd09fdeploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
- becbd59bcomponents#13: modal dialog (<dialog> based, focus wrap, backdrop/Esc/x close with reason, scroll lock, footer buttons)mre
- 367dd19adeploy.sh: never send .git or .gitignore to Byrodinmre
- 4b4dabe5State of 2026-09-27, before the move to gitoriamre