{"id":23192847,"url":"https://github.com/prantlf/piwo","last_synced_at":"2026-02-05T21:01:32.830Z","repository":{"id":268567202,"uuid":"904773387","full_name":"prantlf/piwo","owner":"prantlf","description":"Pico Web Components. Experimental web components. A demonstration of the technology rather than a library for building applications. But that may change :-)","archived":false,"fork":false,"pushed_at":"2025-01-05T20:52:17.000Z","size":1470,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-25T11:44:55.410Z","etag":null,"topics":["web-components"],"latest_commit_sha":null,"homepage":"https://prantlf.github.io/piwo/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prantlf.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-12-17T14:23:08.000Z","updated_at":"2025-08-24T00:05:53.000Z","dependencies_parsed_at":"2025-10-25T11:33:04.409Z","dependency_job_id":"a4782e2a-1177-4496-854d-52933743ef2a","html_url":"https://github.com/prantlf/piwo","commit_stats":null,"previous_names":["prantlf/piwo"],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/prantlf/piwo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prantlf%2Fpiwo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prantlf%2Fpiwo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prantlf%2Fpiwo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prantlf%2Fpiwo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prantlf","download_url":"https://codeload.github.com/prantlf/piwo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prantlf%2Fpiwo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29134201,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-05T20:50:26.975Z","status":"ssl_error","status_checked_at":"2026-02-05T20:49:26.082Z","response_time":65,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["web-components"],"created_at":"2024-12-18T13:09:16.815Z","updated_at":"2026-02-05T21:01:32.808Z","avatar_url":"https://github.com/prantlf.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pico Web Components\n\nExperimental web components. A demonstration of the technology rather than a library for building applications. But that may change :-)\n\n* Low-level. Granularity of native elements for fully flexible layouts and combinations with the native elements.\n* Tiny. Avoid dependencies on heavy frameworks when writing small web components.\n* Integrated. Usable interchangeably with the native HTML elements by providing the same interface.\n* Accessible. Support assistive technologies like screen readers by the form-associated interface.\n* Styled. When used alone, they don't need additional styles to compose a good layout.\n* Protected. Shadow DOM makes the inner styles resistant against external stylesheets.\n\nStyles aren't cleaned up yet. It's just a copy of a part of [Pico CSS]. Refactoring to improve and share code among the components will be done after more concepts are evaluated.\n\n* [Motivation](#motivation)\n* [Usage](#usage)\n* [Example](#example)\n* [Components](#components)\n* [Findings](#findings)\n* [Contributing](#contributing)\n* [License](#license)\n\nSee also live examples: [this page with examples rendered](https://prantlf.github.io/piwo/), [Search](https://prantlf.github.io/piwo/search.html), [Login](https://prantlf.github.io/piwo/login.html), [Widget](https://prantlf.github.io/piwo/widget.html) and [Person](https://prantlf.github.io/piwo/person.html). Pages initialising forms from a schema: [Search](https://prantlf.github.io/piwo/search-schema.html), [Login](https://prantlf.github.io/piwo/login-schema.html), [Widget](https://prantlf.github.io/piwo/widget-schema.html) and [Person](https://prantlf.github.io/piwo/person-schema.html). A picture of the Widget form:\n\n![Widget Form](./docs/widget.png)\n\n## Motivation\n\n* Prototype lightweight low-level web components, which can be used interchangeably with the native HTML elements.\n* Check feasibility of such implementation with the current state of the web component support.\n* Avoid unnecessary complexity of wrapping native elements with the same role in the shadow DOM and then synchronising their states, when it's possible to implement the form-associated interface on the host element.\n\n## Usage\n\nYou can install the package locally to get access to the distributed files:\n\n```\nnpm i -D piwecko\n```\n\nAnd load the web components on a testing page:\n\n```html\n\u003cscript src=\"node_modules/piwecko/dist/index.min.mjs\" type=\"module\"\u003e\u003c/script\u003e\n```\n\nAt this time, this package is more for experimenting with and debugging. You can download it, build it and debug the included pages:\n\n```\ngit clone https://github.com/prantlf/piwo.git\ncd piwo\nbun i --frozen-lockfile\nbun run start\nopen http://localhost:8080/examples/login.html\n```\n\n## Example\n\n```html\n\u003cheader\u003e\n  \u003cpiwo-h level=\"1\"\u003eLogin\u003c/piwo-h\u003e\n  \u003cpiwo-p\u003eProvide your credentials to authenticate to the web application.\u003c/piwo-p\u003e\n\u003c/header\u003e\n\n\u003cmain\u003e\n  \u003cform id=\"login\"\u003e\n    \u003cpiwo-label for=\"name\"\u003eName:\u003c/piwo-label\u003e\n    \u003cpiwo-input name=\"name\" type=\"text\" placeholder=\"Enter your login name\"\n                id=\"name\" aria-describedby=\"name-msg\"\n                required describeerror focuserror\u003e\u003c/piwo-input\u003e\n    \u003cpiwo-small id=\"name-msg\"\u003eAsk the admin about your user name.\u003c/piwo-small\u003e\n\n    \u003cpiwo-label\u003e\n      Password:\n      \u003cpiwo-input name=\"password\" type=\"password\"\n                  placeholder=\"Enter your password\"\u003e\u003c/piwo-input\u003e\n    \u003c/piwo-label\u003e\n\n    \u003cpiwo-label\u003e\n      \u003cpiwo-checkbox name=\"remember-me\"\u003e\u003c/piwo-checkbox\u003e\n      Rememeber me\n    \u003c/piwo-label\u003e\n\n    \u003cpiwo-spacer\u003e\u003c/piwo-spacer\u003e\n\n    \u003cpiwo-label\u003e\n      Comment:\n      \u003cpiwo-textarea name=\"comment\" placeholder=\"Add a comment, if you want\"\u003e\u003c/piwo-textarea\u003e\n    \u003c/piwo-label\u003e\n\n    \u003cpiwo-spacer\u003e\u003c/piwo-spacer\u003e\n\n    \u003cpiwo-button\u003eSubmit\u003c/piwo-button\u003e\n    \u003cpiwo-button type=\"reset\" outline\u003eReset\u003c/piwo-button\u003e\n  \u003c/form\u003e\n\u003c/main\u003e\n```\n\n## Components\n\n### Button\n\nButton for general usage and for submitting forms.\n\n* Lightweight - only a slot for inner content. The host element itself has the role `button`.\n* Form-associated element. Can be used like `input[type=submit]` for submitting forms.\n* Implements many attributes of the native element.\n* Supports the events `click` fired also by keys `Enter`and `Space`.\n* Resets the form if it is `type=reset`.\n* Exposes the state `disabled` in CSS.\n\n```html\n\u003cpiwo-button\u003ePrimary\u003c/piwo-button\u003e\n\u003cpiwo-button kind=\"secondary\"\u003eSecondary\u003c/piwo-button\u003e\n\u003cpiwo-button kind=\"contrast\"\u003eContrast\u003c/piwo-button\u003e\n\n\u003cbr\u003e\n\n\u003cpiwo-button outline\u003ePrimary outline\u003c/piwo-button\u003e\n\u003cpiwo-button kind=\"secondary\" outline\u003eSecondary outline\u003c/piwo-button\u003e\n\u003cpiwo-button kind=\"contrast\" outline\u003eContrast outline\u003c/piwo-button\u003e\n```\n\n### Label\n\nLabel for form fields.\n\n* Lightweight - only a slot for inner content. The host element has no role.\n* Can be used just like the native `label` - by wrapping the field in its body, by pointing to a field by the `for` attribute, or by having the field point to it by the `aria-labelledby` attribute.\n* Attributes and element content can be modified any time later.\n* Clicks are forwarded to the target field.\n\n```html\n\u003cpiwo-label for=\"first-name\"\u003eFirst Name:\u003c/piwo-label\u003e\n\u003cpiwo-input name=\"first-name\" id=\"first-name\" type=\"text\"\u003e\u003c/piwo-input\u003e\n```\n\n### FieldSet\n\nSet of form fields.\n\n* Lightweight - only a slot for inner content. The host element has the role `group`.\n* Can be used just like the native `fieldset` - by wrapping a `pico-legend` and fields in its body, or by pointing to a `pico-legend` by the `aria-labelledby` attribute.\n* Attributes and element content can be modified any time later.\n\n```html\n\u003cpiwo-fieldset\u003e\n  \u003cpiwo-legend\u003ePersonal information\u003c/piwo-legend\u003e\n\u003c/piwo-fieldset\u003e\n```\n\n### Legend\n\nLegend for a set of form fields.\n\n* Lightweight - only a slot for inner content. The host element has no role.\n* Can be used just like the native `legend` - by placing it to  a `pico-fieldset`, or by pointing a `piwo-fieldset` to it by the `aria-labelledby` attribute.\n\nSee [FieldSet](#fieldset) for an example.\n\n### Checkbox\n\nCheckbox form field.\n\n* Lightweight - only an SVG image in the inner content. The host element itself has the role `checkbox`.\n* Form-associated element. Can be used like `input[type=checkbox]`.\n* Implements many attributes of the native element.\n* Supports events `click`, `beforeinput`, `input` and `change` including `preventDefault`.\n* Fills the value `on` or nothing to `FormData`.\n* Remembers the current state for back and forth navigation in the browser.\n* Can reset the value on form `reset`.\n* Exposes states `checked`, `indeterminate`, `disabled`, `readonly` and `required` in CSS.\n* Validates input including custom errors.\n* Emphasises valid or invalid state if `aria-invalid` is set explicitly.\n* Allows either showing the browser popup, or a HTML message for invalid fields, if `describeerror` is set and the last ID in `aria-describedby` points to an element with content. If the element is hidden, it'll be temporarily shown.\n\n```html\n\u003cpiwo-checkbox id=\"unchecked\"\u003e\u003c/piwo-checkbox\u003e\n\u003cpiwo-label for=\"unchecked\"\u003eUnchecked\u003c/piwo-label\u003e\n\n\u003cpiwo-checkbox id=\"checked\" checked\u003e\u003c/piwo-checkbox\u003e\n\u003cpiwo-label for=\"checked\"\u003eChecked\u003c/piwo-label\u003e\n\n\u003cpiwo-checkbox id=\"unchecked-invalid\" required aria-invalid=\"true\"\u003e\u003c/piwo-checkbox\u003e\n\u003cpiwo-label for=\"unchecked-invalid\"\u003eInvalid\u003c/piwo-label\u003e\n\n\u003cpiwo-checkbox id=\"checked-valid\" checked aria-invalid=\"false\"\u003e\u003c/piwo-checkbox\u003e\n\u003cpiwo-label for=\"checked-valid\"\u003eValid\u003c/piwo-label\u003e\n\n\u003cbr\u003e\u003cbr\u003e\n\n\u003cpiwo-label\u003e\n  \u003cpiwo-checkbox indeterminate\u003e\u003c/piwo-checkbox\u003e\n  Indeterminate\n\u003c/piwo-label\u003e\n```\n\n### Switch\n\nSwitch form field.\n\n* Lightweight - no inner content. The host element itself has the role `switch`.\n* Form-associated element. Can be used like `input[type=checkbox]`.\n* Implements many attributes of the native element.\n* Supports events `click`, `beforeinput`, `input` and `change` including `preventDefault`.\n* Fills the value `on` or nothing to `FormData`.\n* Remembers the current state for back and forth navigation in the browser.\n* Can reset the value on form `reset`.\n* Exposes states `checked`, `disabled`, `readonly` and `required` in CSS.\n* Validates input including custom errors.\n* Emphasises valid or invalid state if `aria-invalid` is set explicitly.\n* Allows either showing the browser popup, or a HTML message for invalid fields, if `describeerror` is set and the last ID in `aria-describedby` points to an element with content. If the element is hidden, it'll be temporarily shown.\n\n```html\n\u003cpiwo-switch id=\"off\"\u003e\u003c/piwo-switch\u003e\n\u003cpiwo-label for=\"off\"\u003eOff\u003c/piwo-label\u003e\n\n\u003cpiwo-switch id=\"on\" checked\u003e\u003c/piwo-switch\u003e\n\u003cpiwo-label for=\"on\"\u003eOn\u003c/piwo-label\u003e\n\n\u003cpiwo-switch id=\"invalid-off\" required aria-invalid=\"true\"\u003e\u003c/piwo-switch\u003e\n\u003cpiwo-label for=\"invalid-off\"\u003eInvalid\u003c/piwo-label\u003e\n\n\u003cpiwo-switch id=\"valid-on\" checked aria-invalid=\"false\"\u003e\u003c/piwo-switch\u003e\n\u003cpiwo-label for=\"valid-on\"\u003eValid\u003c/piwo-label\u003e\n```\n\n### Radio\n\nRadio form field.\n\n* Lightweight - an empty element with a stylesheet. The host element itself has the role `radio`.\n* Form-associated element. Can be used like `input[type=radio]`.\n* Implements many attributes of the native element.\n* Supports events `click`, `beforeinput`, `input` and `change` including `preventDefault`.\n* Fills the value of the checked element to `FormData`.\n* Remembers the current state for back and forth navigation in the browser.\n* Can reset the value on form `reset`.\n* Exposes states `checked`, `disabled`, `readonly` and `required` in CSS.\n* Validates input including custom errors.\n* Emphasises valid or invalid state if `aria-invalid` is set explicitly.\n* Allows either showing the browser popup, or a HTML message for invalid fields, if `describeerror` is set and the last ID in `aria-describedby` points to an element with content. If the element is hidden, it'll be temporarily shown.\n\n```html\n\u003cpiwo-label\u003e\n  \u003cpiwo-radio name=\"marital-status\" value=\"single\" checked\u003e\u003c/piwo-radio\u003e\n  Single\n\u003c/piwo-label\u003e\n\u003cpiwo-label\u003e\n  \u003cpiwo-radio name=\"marital-status\" value=\"married\"\u003e\u003c/piwo-radio\u003e\n  Married\n\u003c/piwo-label\u003e\n\n\u003cbr\u003e\n\n\u003cpiwo-label\u003e\n  \u003cpiwo-radio name=\"marital-status-invalid\" value=\"single\" required aria-invalid=\"true\"\u003e\u003c/piwo-radio\u003e\n  Single\n\u003c/piwo-label\u003e\n\u003cpiwo-label\u003e\n  \u003cpiwo-radio name=\"marital-status-invalid\" value=\"married\" required\u003e\u003c/piwo-radio\u003e\n  Married\n\u003c/piwo-label\u003e\n\n\u003cbr\u003e\n\n\u003cpiwo-label\u003e\n  \u003cpiwo-radio name=\"marital-status-valid\" value=\"single\" required\u003e\u003c/piwo-radio\u003e\n  Single\n\u003c/piwo-label\u003e\n\u003cpiwo-label\u003e\n  \u003cpiwo-radio name=\"marital-status-valid\" value=\"married\" checked required aria-invalid=\"false\"\u003e\u003c/piwo-radio\u003e\n  Married\n\u003c/piwo-label\u003e\n```\n\n### Select\n\nSelect form field.\n\n* Uses a native `select` element for the selecting functionality. Hides it from accessible technologies. (This may yet change.) The host element itself has the role `listbox`.\n* Form-associated element. Can be used like `select` with `option` and `optgroup` children.\n* Implements many attributes of the native element.\n* Supports events `click`, `beforeinput`, `input` and `change` including `preventDefault`.\n* Fills the value to `FormData`.\n* Exposes states `disabled`, `readonly` and `required` in CSS.\n* Validates input including custom errors.\n* Emphasises valid or invalid state if `aria-invalid` is set explicitly.\n* Allows either showing the browser popup, or a HTML message for invalid fields, if `describeerror` is set and the last ID in `aria-describedby` points to an element with content. If the element is hidden, it'll be temporarily shown.\n\n```html\n\u003cpiwo-select name=\"fruit\"\u003e\n  \u003coption value=\"apples\" selected\u003eApples\u003c/option\u003e\n  \u003coption value=\"oranges\"\u003eOranges\u003c/option\u003e\n\u003c/piwo-select\u003e\n\n\u003cpiwo-select name=\"unselected-fruit\" required aria-invalid=\"true\"\u003e\n  \u003coption value=\"\" disabled selected hidden\u003eSelect a fruit\u003c/option\u003e\n  \u003coption value=\"apples\"\u003eApples\u003c/option\u003e\n  \u003coption value=\"oranges\"\u003eOranges\u003c/option\u003e\n\u003c/piwo-select\u003e\n\n\u003cpiwo-select name=\"selected-fruit\" required aria-invalid=\"false\"\u003e\n  \u003coption value=\"\" disabled hidden\u003eSelect a fruit\u003c/option\u003e\n  \u003coption value=\"apples\"\u003eApples\u003c/option\u003e\n  \u003coption value=\"oranges\" selected\u003eOranges\u003c/option\u003e\n\u003c/piwo-select\u003e\n\n\u003cpiwo-select name=\"fruit\" multiple\u003e\n  \u003coption\u003eAfrica\u003c/option\u003e\n  \u003coption\u003eAntarktica\u003c/option\u003e\n  \u003coption selected\u003eAsia\u003c/option\u003e\n  \u003coption\u003eAustralia and Oceania\u003c/option\u003e\n  \u003coption selected\u003eEurope\u003c/option\u003e\n  \u003coption\u003eNorth America\u003c/option\u003e\n  \u003coption\u003eSouth America\u003c/option\u003e\n\u003c/piwo-select\u003e\n```\n\n### Input\n\nInput form field.\n\n* Uses a native `input` element for the editing functionality. Hides it from accessible technologies. (This may yet change.) The host element itself has the role `textbox`.\n* Form-associated element. Can be used like `input[type=text]` and other types using a textbox for editing.\n* Implements many attributes of the native element.\n* Can be supplied with type-ahead values or range tick-marks in `datalist` put into a `data` sklot.\n* Supports types `color`, `date`, `datetime-local`, `email`, `file`, `month`, `number`, `password`, `range`, `search`, `tel`, `text`, `time`, `url` and `week`.\n* Supports events `click`, `beforeinput`, `input` and `change` including `preventDefault`.\n* Fills the value to `FormData`.\n* Remembers the current value for back and forth navigation in the browser.\n* Can reset the value on form `reset`.\n* Exposes states `disabled`, `empty`, `readonly` and `required` in CSS.\n* Validates input including custom errors.\n* Emphasises valid or invalid state if `aria-invalid` is set explicitly.\n* Allows either showing the browser popup, or a HTML message for invalid fields, if `describeerror` is set and the last ID in `aria-describedby` points to an element with content. If the element is hidden, it'll be temporarily shown.\n\n```html\n\u003cpiwo-input name=\"last-name\" type=\"text\" aria-label=\"Last name\"\n            placeholder=\"Enter your last name\"\u003e\u003c/piwo-input\u003e\n\u003cpiwo-input name=\"missing-last-name\" type=\"text\" aria-label=\"Last name\"\n            placeholder=\"Enter your last name\" aria-invalid=\"true\"\n            required\u003e\u003c/piwo-input\u003e\n\u003cpiwo-input name=\"entered-last-name\" type=\"text\" aria-label=\"Last name\"\n            placeholder=\"Enter your last name\" aria-invalid=\"false\"\n            required value=\"Doe\"\u003e\u003c/piwo-input\u003e\n\n\u003cbr\u003e\u003cbr\u003e\n\n\u003cpiwo-input name=\"email\" type=\"email\" aria-label=\"E-mail address\"\n            placeholder=\"Enter an e-mail address\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"url\" type=\"url\" aria-label=\"Url\"\n            placeholder=\"Enter a URL\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"phone\" type=\"tel\" aria-label=\"Phone number\"\n            placeholder=\"Enter a phone number\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"date\" type=\"date\" aria-label=\"Date\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"date-time\" type=\"datetime-local\" aria-label=\"Date and time\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"time\" type=\"time\" aria-label=\"Time\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"month\" type=\"month\" aria-label=\"Month\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"week\" type=\"week\" aria-label=\"Week\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"secret\" type=\"password\" aria-label=\"Secret\"\n            placeholder=\"Enter your secret\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"color\" type=\"color\" aria-label=\"Your favourite colour\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"range\" type=\"range\" aria-label=\"Range\" min=\"0\" max=\"3\" step=\"1\"\u003e\u003c/piwo-input\u003e\n\n\u003cpiwo-input name=\"grade\" type=\"range\" aria-label=\"Grade\" min=\"1\" max=\"3\" step=\"1\" ticks\u003e\n  \u003cdatalist slot=\"data\"\u003e\n    \u003coption value=\"1\"\u003eMinimum\u003c/option\u003e\n    \u003coption value=\"2\"\u003eNormal\u003c/option\u003e\n    \u003coption value=\"3\"\u003eMaximum\u003c/option\u003e\n  \u003c/datalist\u003e\n\u003c/piwo-input\u003e\n```\n\n### TextArea\n\nTextArea form field.\n\n* Lightweight - only a slot for the edited text. The host element itself has the role `textbox`.\n* Form-associated element. Can be used like `textarea`.\n* Implements many attributes of the native element.\n* Supports events `beforeinput`, `input` and `change` including `preventDefault`.\n* Fills the value to `FormData`.\n* Remembers the current value for back and forth navigation in the browser.\n* Can reset the value on form `reset`.\n* Exposes states `disabled`, `empty`, `readonly` and `required` in CSS.\n* Validates input including custom errors.\n* Emphasises valid or invalid state if `aria-invalid` is set explicitly.\n* Allows either showing the browser popup, or a HTML message for invalid fields, if `describeerror` is set and the last ID in `aria-describedby` points to an element with content. If the element is hidden, it'll be temporarily shown.\n\n```html\n\u003cpiwo-textarea name=\"review\" aria-label=\"Review\" placeholder=\"Write a review\"\u003e\u003c/piwo-textarea\u003e\n\u003cpiwo-textarea name=\"missing-review\" aria-label=\"Review\" placeholder=\"Write a review\"\n               aria-invalid=\"true\" required\u003e\u003c/piwo-textarea\u003e\n\u003cpiwo-textarea name=\"entered-review\" aria-label=\"Review\" placeholder=\"Write a review\"\n               aria-invalid=\"false\" required\u003eI like it.\u003c/piwo-textarea\u003e\n```\n\n### Small\n\nSmall text, which serves as an information or error message for form fields.\n\n* Lightweight - only a slot for inner content. The host element has no role.\n* Multi-purpose. Information message can be temporarily replaced by the error message.\n* Can be used just like the native elements - by having the field point to it by the `aria-describedby` attribute.\n\n```html\n\u003cpiwo-small\u003e© 2024 Ferdinand Prantl\u003c/piwo-small\u003e\n\n\u003cbr\u003e\u003cbr\u003e\n\n\u003cpiwo-input name=\"user-name\" type=\"text\" placeholder=\"Enter your user name\"\n            aria-label=\"User name\" aria-describedby=\"user-name-msg\"\u003e\u003c/piwo-input\u003e\n\u003cpiwo-small id=\"user-name-msg\"\u003eAsk the admin about your user name.\u003c/piwo-small\u003e\n\n\u003cpiwo-input name=\"login-name\" type=\"text\" placeholder=\"Enter your login name\"\n            aria-label=\"Login name\" aria-describedby=\"login-name-msg\"\n            aria-invalid=\"true\" required describeerror\u003e\u003c/piwo-input\u003e\n\u003cpiwo-small id=\"login-name-msg\"\u003eAsk the admin about your login name.\u003c/piwo-small\u003e\n```\n\n### Heading\n\nHeading element of the level 1 - 6 aligned with `h1` - `h6`.\n\n* Lightweight - only a slot for inner content. The host element has the `heading` role with the corresponding level.\n\n```html\n\u003cpiwo-h level=\"1\"\u003eTitle\u003c/piwo-h\u003e\n```\n\n### Paragraph\n\nParagraph element.\n\n* Lightweight - only a slot for inner content. The host element has the `paragraph` role.\n\n```html\n\u003cpiwo-p\u003eProvide your credentials to authenticate to the web application.\u003c/piwo-p\u003e\n```\n\n### Link\n\nA hyperlink.\n\n* Lightweight - only a slot for inner content. The host element itself has the role `link`.\n\n```html\n\u003cpiwo-link href=\"/\"\u003ePrimary\u003c/piwo-link\u003e\n\u003cpiwo-link href=\"/\" kind=\"secondary\"\u003eSecondary\u003c/piwo-link\u003e\n\u003cpiwo-link href=\"/\" kind=\"contrast\"\u003eContrast\u003c/piwo-link\u003e\n```\n\n### Container\n\nPresentation element ensuring fixed responsive width.\n\n* Lightweight - only a slot for inner content. The host element has no role.\n\n```html\n\u003cpiwo-container\u003e\n  \u003cmain\u003e\n    ...\n  \u003c/main\u003e\n\u003c/piwo-container\u003e\n```\n\n### Horizontal Rule\n\nVertical line separator.\n\n* Lightweight - no content. The host element has the `separator` role.\n\n```html\n\u003cpiwo-hr\u003e\u003c/piwo-hr\u003e\n```\n\n### Spacer\n\nInserts empty vertical space.\n\n* Lightweight - no content. The host element has no role.\n\n```html\n\u003cpiwo-spacer\u003e\u003c/piwo-spacer\u003e\n```\n\n## Findings\n\nMost of the problems found when implementing the custom elements were solved by adding custom code. Only Submitter and Validity Anchor have ugly workarounds. Wrapping elements with children like `select` or `datalist` is problematic, involving element clones.\n\n### Wrapping\n\nWrapping of native elements should be avoided, because they require a specific element structure (for example, `select` - `option` relation), or references by the `ID` attribute (for example, `input` - `datalist`). Creating shadow DOM in a host element breaks the parent - child element relation and disallows referencing elements on the page by `ID`. A workaround is cloning or moving the elements from the main page to the shadow DOM. Luckily it's doable for data-only elements like `option` and `optgroup`.\n\n### Tab Index\n\nFocusability of an element [cannot be declared yet](https://github.com/WICG/webcomponents/issues/762). The attribute `tabindex` has to be set to zero as a workaround. It makes the attribute visible on the element in DOM. Otherwise no problem.\n\n### Label\n\nThe role of a [label cannot be delegated yet](https://github.com/WICG/webcomponents/issues/917), but the click-functionality and the property `labels` can be implemented by custom code. No problem.\n\n### Placeholder\n\nAlthough the placeholder can be specified by `ariaPlaceholder` from `ElementInternals`, it will be used only by the assistive technologies. For sighted users, The placeholder has to be displayed by the implementation of the custom element. It's possible by pure CSS. So, no problem.\n\n### Change Event\n\nWhen using a `contenteditable` custom element as the host, the `change` event should be dispatched on `blur`. Unlike with teh native elements, the event will be dispatched after the blur event, because there's no `beforeblur` event to hook in. Not a big deal.\n\n### Preventing Default Behaviour\n\nTo be able to support `event.defaultPrevented`, `input` and `change` events on mouse and keyboard interactions are triggered asynchronously by `setTimeout`. (The mouse and keyboard handlers registered by the custom element are triggered at first.) Native elements process the interaction (probably) synchronously. Probably not a big deal.\n\n### Submitter\n\nWhen calling `requestSubmit(submitter)` on a form, `submitter` has to be an `input` element. It cannot be just a form-associated custom element. A possible workaround is to create a temporary `input` element and optionally assign it a `name `attribute, when the submit is triggered by custom button. It's a problem, because `event.explicitOriginalTarget` won't be set to the actual button, but to the temporary element. A custom interface can be provided, like setting `form.submitter` to the `input` element and `form.submitter.button` to the actual button, but it's different from the native interface.\n\nOn the other hand, the submitter doesn't have to be read dynamically using the event or the form. Forms usually have well-known submit buttons. And forms with more submit buttons assign a unique value to the `name` attribute to be able to recognise the button in `FormData` on the client or on the server side.\n\n### Validity Anchor\n\nWhen calling `setValidity` from `ElementInternals` with an anchor, this anchor has to be an input element. It cannot be just a form-associated custom element. A clumsy workaround is to create a hidden `input` element and make it visible when the custom element becomes invalid. If it's zero-sized, it won't be visible, but it'll be focusable, when hitting the `Tab` key in the invalid element. It's a problem, waiting for a better workaround.\n\nOn the other hand, real-world forms use error placeholders referred to by `aria-describedby` anyway. I added automatic showing and hiding of the error placeholder (`describeerror`) and focusing of the first invalid field in the form (`focuserror`) beyond the native element functionality for convenience and also to provide a good workaround for the not working anchor.\n\n### Checkbox\n\nBetter than the native element, the custom element can support the `readonly` attribute and the `indeterminate` attribute in the markup. Great!\n\n## Form from Schema\n\nThere're methods provided to generate HTML markup for a form from [JSON Schema] and [Alpaca] extension and for extracting an object with field values from `FormData` compliant with the schema.\n\n```js\nimport { appendFormContent, getFormValues, registerCustomValidation } from 'piwecko'\n\nconst data = {}\nconst schema = {\n  properties: {\n    name: { type: 'string', title: 'Name', required: true },\n    password: { type: 'string', title: 'Password', required: true }\n  }\n}\nconst options = {\n  fields: {\n    name: { placeholder: 'Enter your login name' },\n    password: {\n      type: 'password',\n      placeholder: 'Enter your password',\n      helper: 'The password has to be at least 8 characters long, ' +\n              'include at least 1 lower-case letter, ' +\n              '1 upper-case letter and 1 number.'\n    }\n  }\n}\nconst form = {\n  buttons: {\n    submit: { title: 'Submit' },\n    reset: { title: 'Reset', type: 'reset' }\n  }\n}\nappendFormContent(formElement, { data, schema, options, form })\n\nregisterCustomValidation(formElement, 'password', ({ value }) =\u003e {\n  if (value.length \u003c 8) return 'The password has to be at least 8 characters long.'\n  if (!/[a-z]/.test(value)) return 'The password has to include at least 1 lower-case letter.'\n  if (!/[A-Z]/.test(value)) return 'The password has to include at least 1 upper-case letter.'\n  if (!/[0-9]/.test(value)) return 'The password has to include at least 1 number.'\n})\n\nformElement.addEventListener('submit', event =\u003e {\n  event.preventDefault()\n  const values = getFormValues(event.target, schema)\n  console.log(`Form values:`, values)\n})\n```\n\n## Contributing\n\nIn lieu of a formal styleguide, take care to maintain the existing coding style. Lint and test your code.\n\n## License\n\nCopyright (C) 2024-2025 Ferdinand Prantl\n\nLicensed under the [MIT License].\n\n[MIT License]: http://en.wikipedia.org/wiki/MIT_License\n[Pico CSS]: https://picocss.com/\n[JSON Schema]: https://json-schema.org/\n[Alpaca]: http://www.alpacajs.org/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprantlf%2Fpiwo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprantlf%2Fpiwo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprantlf%2Fpiwo/lists"}