@taprootio/espalier 2.15.3 → 2.16.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.
@@ -21879,6 +21879,368 @@
21879
21879
  }
21880
21880
  ]
21881
21881
  },
21882
+ {
21883
+ "kind": "javascript-module",
21884
+ "path": "dist/form/esp-form.js",
21885
+ "declarations": [
21886
+ {
21887
+ "kind": "class",
21888
+ "description": "A form wrapper that renders a native `<form>` element in\nthe light DOM. All form-associated custom elements placed\ninside participate natively in form submission, validation,\nand reset.\n\n`esp-form` has no visual presentation of its own — all\nstyling, labels, and layout should be handled independently\n(e.g. via [esp-form-item](/components/form-item), `esp-box`,\nor plain CSS).\n\n### Standard submission\n\n```html\n<esp-form action=\"/api/save\" method=\"post\" label=\"Contact form\">\n <esp-form-item label=\"Name\">\n <esp-input name=\"name\" required></esp-input>\n </esp-form-item>\n <esp-button button-type=\"submit\" label=\"Send\"></esp-button>\n</esp-form>\n```\n\n### Fetch submission\n\n```html\n<esp-form action=\"/api/save\" use-fetch use-json label=\"Settings\">\n <esp-form-item label=\"Email\">\n <esp-input name=\"email\" input-type=\"email\" required></esp-input>\n </esp-form-item>\n <esp-button button-type=\"submit\" label=\"Save\"></esp-button>\n</esp-form>\n```\n\n### Dialog integration\n\nWhen `method=\"dialog\"`, submitting the form dispatches a\n`closeDialog` event that `esp-dialog` listens for, closing\nthe dialog without a network request.\n\n### Multi-field form with validation\n\nRequired fields are validated on submit. The first invalid\nfield is scrolled into view and focused. Errors clear as\nthe user corrects each field. Use the `required-message`\nattribute on any form control to customize the error text.\n\n```html\n<style>\nesp-form.signup-demo { display: grid; gap: var(--esp-size-small); }\nesp-form.signup-demo .actions { display: flex; gap: var(--esp-size-small); }\n</style>\n<esp-form class=\"signup-demo\" use-fetch use-json action=\"/api/signup\" label=\"Sign up\">\n <esp-form-item label=\"Full name\">\n <esp-input name=\"fullName\" required required-message=\"We need your full name.\"></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Email address\">\n <esp-input name=\"email\" input-type=\"email\" required></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Password\">\n <esp-input name=\"password\" input-type=\"password\" required required-message=\"You must enter a password to continue!\"></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Favorite color\">\n <esp-pick-one name=\"color\" required placeholder=\"Pick one...\" required-message=\"Please choose your favorite color.\">\n <esp-picker-item text=\"Red\" value=\"red\"></esp-picker-item>\n <esp-picker-item text=\"Green\" value=\"green\"></esp-picker-item>\n <esp-picker-item text=\"Blue\" value=\"blue\"></esp-picker-item>\n </esp-pick-one>\n </esp-form-item>\n <esp-form-item label=\"I agree to the terms\">\n <esp-checkbox name=\"terms\" value=\"agreed\" required required-message=\"You must accept the terms to continue.\">\n Yes, I accept\n </esp-checkbox>\n </esp-form-item>\n <div class=\"actions\">\n <esp-button button-type=\"submit\" label=\"Sign Up\"></esp-button>\n <esp-button button-type=\"reset\" label=\"Reset\" variant=\"danger\"></esp-button>\n </div>\n</esp-form>\n```\n\n### Skipping validation\n\nAdd `formnovalidate` to a submit button to bypass constraint\nvalidation. This is useful for \"Save Draft\" buttons that\nshould persist incomplete data.\n\n```html\n<style>\nesp-form.draft-demo { display: grid; gap: var(--esp-size-small); }\nesp-form.draft-demo .actions { display: flex; gap: var(--esp-size-small); }\n</style>\n<esp-form class=\"draft-demo\" use-fetch use-json action=\"/api/drafts\" label=\"Article editor\">\n <esp-form-item label=\"Title\">\n <esp-input name=\"title\" required></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Category\">\n <esp-pick-one name=\"category\" required placeholder=\"Select...\">\n <esp-picker-item text=\"Technology\" value=\"tech\"></esp-picker-item>\n <esp-picker-item text=\"Design\" value=\"design\"></esp-picker-item>\n <esp-picker-item text=\"Business\" value=\"biz\"></esp-picker-item>\n </esp-pick-one>\n </esp-form-item>\n <div class=\"actions\">\n <esp-button button-type=\"submit\" label=\"Publish\"></esp-button>\n <esp-button button-type=\"submit\" formnovalidate label=\"Save Draft\" variant=\"split-complementary-left\"></esp-button>\n </div>\n</esp-form>\n```\n\n### Handling the response\n\nListen for `esp-submit-response` and `esp-submit-error` to\nreact to the server's reply when using fetch submission.\n\n```html\n<style>\nesp-form.response-demo { display: grid; gap: var(--esp-size-small); }\n</style>\n<esp-form class=\"response-demo\" use-fetch use-json action=\"/api/feedback\" label=\"Feedback\">\n <esp-form-item label=\"Your feedback\">\n <esp-input name=\"message\" required></esp-input>\n </esp-form-item>\n <esp-button button-type=\"submit\" label=\"Send Feedback\"></esp-button>\n <esp-info id=\"response-msg\" icon=\"info-i\" style=\"display:none\">\n <span id=\"response-text\"></span>\n </esp-info>\n</esp-form>\n<script>\n const form = findByTagName(\"esp-form\")[0];\n const msg = findById(\"response-msg\");\n const text = findById(\"response-text\");\n form.addEventListener(\"esp-submit-response\", (ev) => {\n msg.style.display = \"\";\n msg.setAttribute(\"variant\", ev.detail.ok ? \"success\" : \"warning\");\n text.textContent = ev.detail.ok\n ? \"Submitted successfully!\"\n : \"Server returned an error.\";\n });\n form.addEventListener(\"esp-submit-error\", () => {\n msg.style.display = \"\";\n msg.setAttribute(\"variant\", \"danger\");\n text.textContent = \"Network error — please try again.\";\n });\n</script>\n```\n\n### Form with diverse input types\n\nCombines email, telephone, number, and date inputs with\nvalidation and custom messages.\n\n```html\n<style>\nesp-form.diverse-demo { display: grid; gap: var(--esp-size-small); }\nesp-form.diverse-demo .actions { display: flex; gap: var(--esp-size-small); }\n</style>\n<esp-form class=\"diverse-demo\" use-fetch use-json action=\"/api/contact\" label=\"Contact info\">\n <esp-form-item label=\"Email\">\n <esp-input name=\"email\" input-type=\"email\" required></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Phone\">\n <esp-input name=\"phone\" input-type=\"tel\"\n tel-localities=\"US CA GB\" required>\n </esp-input>\n </esp-form-item>\n <esp-form-item label=\"Age\">\n <esp-input name=\"age\" input-type=\"number\"\n min=\"0\" max=\"150\" required>\n </esp-input>\n </esp-form-item>\n <esp-form-item label=\"Date of birth\">\n <esp-input name=\"dob\" input-type=\"date\" required></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Comments\">\n <esp-textarea name=\"comments\" rows=\"3\"\n placeholder=\"Any additional comments...\">\n </esp-textarea>\n </esp-form-item>\n <div class=\"actions\">\n <esp-button button-type=\"submit\" label=\"Submit\"></esp-button>\n <esp-button button-type=\"reset\" label=\"Reset\" variant=\"danger\"></esp-button>\n </div>\n</esp-form>\n```",
21889
+ "name": "EspalierForm",
21890
+ "members": [
21891
+ {
21892
+ "kind": "field",
21893
+ "name": "_form",
21894
+ "type": {
21895
+ "text": "HTMLFormElement"
21896
+ },
21897
+ "privacy": "private"
21898
+ },
21899
+ {
21900
+ "kind": "field",
21901
+ "name": "_formReady",
21902
+ "type": {
21903
+ "text": "boolean"
21904
+ },
21905
+ "privacy": "private",
21906
+ "default": "false"
21907
+ },
21908
+ {
21909
+ "kind": "field",
21910
+ "name": "action",
21911
+ "type": {
21912
+ "text": "string"
21913
+ },
21914
+ "privacy": "public",
21915
+ "default": "\"\"",
21916
+ "description": "The URL that processes the form submission. When `use-fetch`\nis true this is the URL `fetch()` sends the request to.",
21917
+ "attribute": "action"
21918
+ },
21919
+ {
21920
+ "kind": "field",
21921
+ "name": "method",
21922
+ "type": {
21923
+ "text": "string"
21924
+ },
21925
+ "privacy": "public",
21926
+ "default": "\"post\"",
21927
+ "description": "The HTTP method for submission. Standard values are `get`,\n`post`, and `dialog`. When set to `dialog`, submitting the\nform closes the nearest `esp-dialog` ancestor without making\na network request.",
21928
+ "attribute": "method"
21929
+ },
21930
+ {
21931
+ "kind": "field",
21932
+ "name": "novalidate",
21933
+ "type": {
21934
+ "text": "boolean"
21935
+ },
21936
+ "privacy": "public",
21937
+ "default": "false",
21938
+ "description": "When true, constraint validation is skipped during\nsubmission.",
21939
+ "attribute": "novalidate"
21940
+ },
21941
+ {
21942
+ "kind": "field",
21943
+ "name": "useFetch",
21944
+ "type": {
21945
+ "text": "boolean"
21946
+ },
21947
+ "privacy": "public",
21948
+ "default": "false",
21949
+ "description": "When true, the form uses `fetch()` instead of native\nbrowser navigation for submission. An `esp-submit` event\nis fired with the `FormData` in the detail, allowing\nconsumers to cancel or modify the request.",
21950
+ "attribute": "use-fetch"
21951
+ },
21952
+ {
21953
+ "kind": "field",
21954
+ "name": "useJson",
21955
+ "type": {
21956
+ "text": "boolean"
21957
+ },
21958
+ "privacy": "public",
21959
+ "default": "false",
21960
+ "description": "When `use-fetch` is true and this is also true, the request\nbody is serialized as JSON instead of `FormData`.",
21961
+ "attribute": "use-json"
21962
+ },
21963
+ {
21964
+ "kind": "field",
21965
+ "name": "enctype",
21966
+ "type": {
21967
+ "text": "string"
21968
+ },
21969
+ "privacy": "public",
21970
+ "default": "\"application/x-www-form-urlencoded\"",
21971
+ "description": "The encoding type for form submission. Maps to the native\n`enctype` attribute on the inner `<form>`.",
21972
+ "attribute": "enctype"
21973
+ },
21974
+ {
21975
+ "kind": "field",
21976
+ "name": "label",
21977
+ "type": {
21978
+ "text": "string"
21979
+ },
21980
+ "privacy": "public",
21981
+ "default": "\"\"",
21982
+ "description": "An accessible label applied as `aria-label` on the inner\n`<form>` element so the form is discoverable as an ARIA\n`form` landmark by screen readers.",
21983
+ "attribute": "label"
21984
+ },
21985
+ {
21986
+ "kind": "method",
21987
+ "name": "syncFormAttributes",
21988
+ "privacy": "private",
21989
+ "return": {
21990
+ "type": {
21991
+ "text": "void"
21992
+ }
21993
+ }
21994
+ },
21995
+ {
21996
+ "kind": "method",
21997
+ "name": "checkValidity",
21998
+ "privacy": "public",
21999
+ "return": {
22000
+ "type": {
22001
+ "text": "boolean"
22002
+ }
22003
+ },
22004
+ "description": "Run constraint validation on all controls without showing\nany UI feedback."
22005
+ },
22006
+ {
22007
+ "kind": "method",
22008
+ "name": "reportValidity",
22009
+ "privacy": "public",
22010
+ "return": {
22011
+ "type": {
22012
+ "text": ""
22013
+ }
22014
+ },
22015
+ "description": "Run constraint validation, display error messages via each\ncontrol's `esp-form-item`, and scroll the first invalid\nitem into view."
22016
+ },
22017
+ {
22018
+ "kind": "method",
22019
+ "name": "reset",
22020
+ "privacy": "public",
22021
+ "return": {
22022
+ "type": {
22023
+ "text": "void"
22024
+ }
22025
+ },
22026
+ "description": "Programmatically reset the form and all its controls."
22027
+ },
22028
+ {
22029
+ "kind": "method",
22030
+ "name": "submit",
22031
+ "privacy": "public",
22032
+ "return": {
22033
+ "type": {
22034
+ "text": "void"
22035
+ }
22036
+ },
22037
+ "description": "Programmatically trigger form submission (with validation)."
22038
+ },
22039
+ {
22040
+ "kind": "method",
22041
+ "name": "handleSubmit",
22042
+ "privacy": "private",
22043
+ "return": {
22044
+ "type": {
22045
+ "text": "void"
22046
+ }
22047
+ },
22048
+ "parameters": [
22049
+ {
22050
+ "name": "ev",
22051
+ "type": {
22052
+ "text": "SubmitEvent"
22053
+ }
22054
+ }
22055
+ ]
22056
+ },
22057
+ {
22058
+ "kind": "method",
22059
+ "name": "handleKeyDown",
22060
+ "privacy": "private",
22061
+ "return": {
22062
+ "type": {
22063
+ "text": "void"
22064
+ }
22065
+ },
22066
+ "parameters": [
22067
+ {
22068
+ "name": "ev",
22069
+ "type": {
22070
+ "text": "KeyboardEvent"
22071
+ }
22072
+ }
22073
+ ]
22074
+ },
22075
+ {
22076
+ "kind": "method",
22077
+ "name": "submitViaFetch",
22078
+ "privacy": "private",
22079
+ "return": {
22080
+ "type": {
22081
+ "text": "Promise<void>"
22082
+ }
22083
+ },
22084
+ "parameters": [
22085
+ {
22086
+ "name": "form",
22087
+ "type": {
22088
+ "text": "HTMLFormElement"
22089
+ }
22090
+ }
22091
+ ]
22092
+ },
22093
+ {
22094
+ "kind": "field",
22095
+ "name": "noValidate",
22096
+ "type": {
22097
+ "text": "boolean"
22098
+ },
22099
+ "default": "true"
22100
+ }
22101
+ ],
22102
+ "events": [
22103
+ {
22104
+ "name": "closeDialog",
22105
+ "type": {
22106
+ "text": "CustomEvent"
22107
+ },
22108
+ "description": "Fired when `method=\"dialog\"` is used and the form requests its containing `<esp-dialog>` to close. The event detail is an empty object."
22109
+ },
22110
+ {
22111
+ "name": "esp-submit-response",
22112
+ "type": {
22113
+ "text": "CustomEvent<{ response: Response; ok: boolean }>"
22114
+ },
22115
+ "description": "Fired after a successful `fetch` submission."
22116
+ },
22117
+ {
22118
+ "name": "esp-submit-error",
22119
+ "type": {
22120
+ "text": "CustomEvent<{ error: unknown }>"
22121
+ },
22122
+ "description": "Fired when a `fetch` submission fails."
22123
+ },
22124
+ {
22125
+ "type": {
22126
+ "text": "CustomEvent<{ formData: FormData; form: HTMLFormElement }>"
22127
+ },
22128
+ "description": "Fired when `use-fetch` is true and the form passes validation. Cancelable; calling `preventDefault()` aborts the fetch.",
22129
+ "name": "esp-submit"
22130
+ }
22131
+ ],
22132
+ "attributes": [
22133
+ {
22134
+ "name": "action",
22135
+ "type": {
22136
+ "text": "string"
22137
+ },
22138
+ "default": "\"\"",
22139
+ "description": "The URL that processes the form submission. When `use-fetch`\nis true this is the URL `fetch()` sends the request to.",
22140
+ "fieldName": "action"
22141
+ },
22142
+ {
22143
+ "name": "method",
22144
+ "type": {
22145
+ "text": "string"
22146
+ },
22147
+ "default": "\"post\"",
22148
+ "description": "The HTTP method for submission. Standard values are `get`,\n`post`, and `dialog`. When set to `dialog`, submitting the\nform closes the nearest `esp-dialog` ancestor without making\na network request.",
22149
+ "fieldName": "method"
22150
+ },
22151
+ {
22152
+ "name": "novalidate",
22153
+ "type": {
22154
+ "text": "boolean"
22155
+ },
22156
+ "default": "false",
22157
+ "description": "When true, constraint validation is skipped during\nsubmission.",
22158
+ "fieldName": "novalidate"
22159
+ },
22160
+ {
22161
+ "name": "use-fetch",
22162
+ "type": {
22163
+ "text": "boolean"
22164
+ },
22165
+ "default": "false",
22166
+ "description": "When true, the form uses `fetch()` instead of native\nbrowser navigation for submission. An `esp-submit` event\nis fired with the `FormData` in the detail, allowing\nconsumers to cancel or modify the request.",
22167
+ "fieldName": "useFetch"
22168
+ },
22169
+ {
22170
+ "name": "use-json",
22171
+ "type": {
22172
+ "text": "boolean"
22173
+ },
22174
+ "default": "false",
22175
+ "description": "When `use-fetch` is true and this is also true, the request\nbody is serialized as JSON instead of `FormData`.",
22176
+ "fieldName": "useJson"
22177
+ },
22178
+ {
22179
+ "name": "enctype",
22180
+ "type": {
22181
+ "text": "string"
22182
+ },
22183
+ "default": "\"application/x-www-form-urlencoded\"",
22184
+ "description": "The encoding type for form submission. Maps to the native\n`enctype` attribute on the inner `<form>`.",
22185
+ "fieldName": "enctype"
22186
+ },
22187
+ {
22188
+ "name": "label",
22189
+ "type": {
22190
+ "text": "string"
22191
+ },
22192
+ "default": "\"\"",
22193
+ "description": "An accessible label applied as `aria-label` on the inner\n`<form>` element so the form is discoverable as an ARIA\n`form` landmark by screen readers.",
22194
+ "fieldName": "label"
22195
+ }
22196
+ ],
22197
+ "superclass": {
22198
+ "name": "LitElement",
22199
+ "package": "lit"
22200
+ },
22201
+ "tagName": "esp-form",
22202
+ "customElement": true,
22203
+ "docPageTitle": {
22204
+ "name": "Form",
22205
+ "description": ""
22206
+ },
22207
+ "docUrl": {
22208
+ "name": "/components/form",
22209
+ "description": ""
22210
+ },
22211
+ "menuGroup": {
22212
+ "name": "Form",
22213
+ "description": "Controls"
22214
+ },
22215
+ "menuLabel": {
22216
+ "name": "Form",
22217
+ "description": ""
22218
+ },
22219
+ "menuIcon": {
22220
+ "name": "forms",
22221
+ "description": ""
22222
+ }
22223
+ }
22224
+ ],
22225
+ "exports": [
22226
+ {
22227
+ "kind": "js",
22228
+ "name": "EspalierForm",
22229
+ "declaration": {
22230
+ "name": "EspalierForm",
22231
+ "module": "dist/form/esp-form.js"
22232
+ }
22233
+ },
22234
+ {
22235
+ "kind": "custom-element-definition",
22236
+ "name": "esp-form",
22237
+ "declaration": {
22238
+ "name": "EspalierForm",
22239
+ "module": "dist/form/esp-form.js"
22240
+ }
22241
+ }
22242
+ ]
22243
+ },
21882
22244
  {
21883
22245
  "kind": "javascript-module",
21884
22246
  "path": "dist/form-item/esp-form-item.js",
@@ -22444,368 +22806,6 @@
22444
22806
  }
22445
22807
  ]
22446
22808
  },
22447
- {
22448
- "kind": "javascript-module",
22449
- "path": "dist/form/esp-form.js",
22450
- "declarations": [
22451
- {
22452
- "kind": "class",
22453
- "description": "A form wrapper that renders a native `<form>` element in\nthe light DOM. All form-associated custom elements placed\ninside participate natively in form submission, validation,\nand reset.\n\n`esp-form` has no visual presentation of its own — all\nstyling, labels, and layout should be handled independently\n(e.g. via [esp-form-item](/components/form-item), `esp-box`,\nor plain CSS).\n\n### Standard submission\n\n```html\n<esp-form action=\"/api/save\" method=\"post\" label=\"Contact form\">\n <esp-form-item label=\"Name\">\n <esp-input name=\"name\" required></esp-input>\n </esp-form-item>\n <esp-button button-type=\"submit\" label=\"Send\"></esp-button>\n</esp-form>\n```\n\n### Fetch submission\n\n```html\n<esp-form action=\"/api/save\" use-fetch use-json label=\"Settings\">\n <esp-form-item label=\"Email\">\n <esp-input name=\"email\" input-type=\"email\" required></esp-input>\n </esp-form-item>\n <esp-button button-type=\"submit\" label=\"Save\"></esp-button>\n</esp-form>\n```\n\n### Dialog integration\n\nWhen `method=\"dialog\"`, submitting the form dispatches a\n`closeDialog` event that `esp-dialog` listens for, closing\nthe dialog without a network request.\n\n### Multi-field form with validation\n\nRequired fields are validated on submit. The first invalid\nfield is scrolled into view and focused. Errors clear as\nthe user corrects each field. Use the `required-message`\nattribute on any form control to customize the error text.\n\n```html\n<style>\nesp-form.signup-demo { display: grid; gap: var(--esp-size-small); }\nesp-form.signup-demo .actions { display: flex; gap: var(--esp-size-small); }\n</style>\n<esp-form class=\"signup-demo\" use-fetch use-json action=\"/api/signup\" label=\"Sign up\">\n <esp-form-item label=\"Full name\">\n <esp-input name=\"fullName\" required required-message=\"We need your full name.\"></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Email address\">\n <esp-input name=\"email\" input-type=\"email\" required></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Password\">\n <esp-input name=\"password\" input-type=\"password\" required required-message=\"You must enter a password to continue!\"></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Favorite color\">\n <esp-pick-one name=\"color\" required placeholder=\"Pick one...\" required-message=\"Please choose your favorite color.\">\n <esp-picker-item text=\"Red\" value=\"red\"></esp-picker-item>\n <esp-picker-item text=\"Green\" value=\"green\"></esp-picker-item>\n <esp-picker-item text=\"Blue\" value=\"blue\"></esp-picker-item>\n </esp-pick-one>\n </esp-form-item>\n <esp-form-item label=\"I agree to the terms\">\n <esp-checkbox name=\"terms\" value=\"agreed\" required required-message=\"You must accept the terms to continue.\">\n Yes, I accept\n </esp-checkbox>\n </esp-form-item>\n <div class=\"actions\">\n <esp-button button-type=\"submit\" label=\"Sign Up\"></esp-button>\n <esp-button button-type=\"reset\" label=\"Reset\" variant=\"danger\"></esp-button>\n </div>\n</esp-form>\n```\n\n### Skipping validation\n\nAdd `formnovalidate` to a submit button to bypass constraint\nvalidation. This is useful for \"Save Draft\" buttons that\nshould persist incomplete data.\n\n```html\n<style>\nesp-form.draft-demo { display: grid; gap: var(--esp-size-small); }\nesp-form.draft-demo .actions { display: flex; gap: var(--esp-size-small); }\n</style>\n<esp-form class=\"draft-demo\" use-fetch use-json action=\"/api/drafts\" label=\"Article editor\">\n <esp-form-item label=\"Title\">\n <esp-input name=\"title\" required></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Category\">\n <esp-pick-one name=\"category\" required placeholder=\"Select...\">\n <esp-picker-item text=\"Technology\" value=\"tech\"></esp-picker-item>\n <esp-picker-item text=\"Design\" value=\"design\"></esp-picker-item>\n <esp-picker-item text=\"Business\" value=\"biz\"></esp-picker-item>\n </esp-pick-one>\n </esp-form-item>\n <div class=\"actions\">\n <esp-button button-type=\"submit\" label=\"Publish\"></esp-button>\n <esp-button button-type=\"submit\" formnovalidate label=\"Save Draft\" variant=\"split-complementary-left\"></esp-button>\n </div>\n</esp-form>\n```\n\n### Handling the response\n\nListen for `esp-submit-response` and `esp-submit-error` to\nreact to the server's reply when using fetch submission.\n\n```html\n<style>\nesp-form.response-demo { display: grid; gap: var(--esp-size-small); }\n</style>\n<esp-form class=\"response-demo\" use-fetch use-json action=\"/api/feedback\" label=\"Feedback\">\n <esp-form-item label=\"Your feedback\">\n <esp-input name=\"message\" required></esp-input>\n </esp-form-item>\n <esp-button button-type=\"submit\" label=\"Send Feedback\"></esp-button>\n <esp-info id=\"response-msg\" icon=\"info-i\" style=\"display:none\">\n <span id=\"response-text\"></span>\n </esp-info>\n</esp-form>\n<script>\n const form = findByTagName(\"esp-form\")[0];\n const msg = findById(\"response-msg\");\n const text = findById(\"response-text\");\n form.addEventListener(\"esp-submit-response\", (ev) => {\n msg.style.display = \"\";\n msg.setAttribute(\"variant\", ev.detail.ok ? \"success\" : \"warning\");\n text.textContent = ev.detail.ok\n ? \"Submitted successfully!\"\n : \"Server returned an error.\";\n });\n form.addEventListener(\"esp-submit-error\", () => {\n msg.style.display = \"\";\n msg.setAttribute(\"variant\", \"danger\");\n text.textContent = \"Network error — please try again.\";\n });\n</script>\n```\n\n### Form with diverse input types\n\nCombines email, telephone, number, and date inputs with\nvalidation and custom messages.\n\n```html\n<style>\nesp-form.diverse-demo { display: grid; gap: var(--esp-size-small); }\nesp-form.diverse-demo .actions { display: flex; gap: var(--esp-size-small); }\n</style>\n<esp-form class=\"diverse-demo\" use-fetch use-json action=\"/api/contact\" label=\"Contact info\">\n <esp-form-item label=\"Email\">\n <esp-input name=\"email\" input-type=\"email\" required></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Phone\">\n <esp-input name=\"phone\" input-type=\"tel\"\n tel-localities=\"US CA GB\" required>\n </esp-input>\n </esp-form-item>\n <esp-form-item label=\"Age\">\n <esp-input name=\"age\" input-type=\"number\"\n min=\"0\" max=\"150\" required>\n </esp-input>\n </esp-form-item>\n <esp-form-item label=\"Date of birth\">\n <esp-input name=\"dob\" input-type=\"date\" required></esp-input>\n </esp-form-item>\n <esp-form-item label=\"Comments\">\n <esp-textarea name=\"comments\" rows=\"3\"\n placeholder=\"Any additional comments...\">\n </esp-textarea>\n </esp-form-item>\n <div class=\"actions\">\n <esp-button button-type=\"submit\" label=\"Submit\"></esp-button>\n <esp-button button-type=\"reset\" label=\"Reset\" variant=\"danger\"></esp-button>\n </div>\n</esp-form>\n```",
22454
- "name": "EspalierForm",
22455
- "members": [
22456
- {
22457
- "kind": "field",
22458
- "name": "_form",
22459
- "type": {
22460
- "text": "HTMLFormElement"
22461
- },
22462
- "privacy": "private"
22463
- },
22464
- {
22465
- "kind": "field",
22466
- "name": "_formReady",
22467
- "type": {
22468
- "text": "boolean"
22469
- },
22470
- "privacy": "private",
22471
- "default": "false"
22472
- },
22473
- {
22474
- "kind": "field",
22475
- "name": "action",
22476
- "type": {
22477
- "text": "string"
22478
- },
22479
- "privacy": "public",
22480
- "default": "\"\"",
22481
- "description": "The URL that processes the form submission. When `use-fetch`\nis true this is the URL `fetch()` sends the request to.",
22482
- "attribute": "action"
22483
- },
22484
- {
22485
- "kind": "field",
22486
- "name": "method",
22487
- "type": {
22488
- "text": "string"
22489
- },
22490
- "privacy": "public",
22491
- "default": "\"post\"",
22492
- "description": "The HTTP method for submission. Standard values are `get`,\n`post`, and `dialog`. When set to `dialog`, submitting the\nform closes the nearest `esp-dialog` ancestor without making\na network request.",
22493
- "attribute": "method"
22494
- },
22495
- {
22496
- "kind": "field",
22497
- "name": "novalidate",
22498
- "type": {
22499
- "text": "boolean"
22500
- },
22501
- "privacy": "public",
22502
- "default": "false",
22503
- "description": "When true, constraint validation is skipped during\nsubmission.",
22504
- "attribute": "novalidate"
22505
- },
22506
- {
22507
- "kind": "field",
22508
- "name": "useFetch",
22509
- "type": {
22510
- "text": "boolean"
22511
- },
22512
- "privacy": "public",
22513
- "default": "false",
22514
- "description": "When true, the form uses `fetch()` instead of native\nbrowser navigation for submission. An `esp-submit` event\nis fired with the `FormData` in the detail, allowing\nconsumers to cancel or modify the request.",
22515
- "attribute": "use-fetch"
22516
- },
22517
- {
22518
- "kind": "field",
22519
- "name": "useJson",
22520
- "type": {
22521
- "text": "boolean"
22522
- },
22523
- "privacy": "public",
22524
- "default": "false",
22525
- "description": "When `use-fetch` is true and this is also true, the request\nbody is serialized as JSON instead of `FormData`.",
22526
- "attribute": "use-json"
22527
- },
22528
- {
22529
- "kind": "field",
22530
- "name": "enctype",
22531
- "type": {
22532
- "text": "string"
22533
- },
22534
- "privacy": "public",
22535
- "default": "\"application/x-www-form-urlencoded\"",
22536
- "description": "The encoding type for form submission. Maps to the native\n`enctype` attribute on the inner `<form>`.",
22537
- "attribute": "enctype"
22538
- },
22539
- {
22540
- "kind": "field",
22541
- "name": "label",
22542
- "type": {
22543
- "text": "string"
22544
- },
22545
- "privacy": "public",
22546
- "default": "\"\"",
22547
- "description": "An accessible label applied as `aria-label` on the inner\n`<form>` element so the form is discoverable as an ARIA\n`form` landmark by screen readers.",
22548
- "attribute": "label"
22549
- },
22550
- {
22551
- "kind": "method",
22552
- "name": "syncFormAttributes",
22553
- "privacy": "private",
22554
- "return": {
22555
- "type": {
22556
- "text": "void"
22557
- }
22558
- }
22559
- },
22560
- {
22561
- "kind": "method",
22562
- "name": "checkValidity",
22563
- "privacy": "public",
22564
- "return": {
22565
- "type": {
22566
- "text": "boolean"
22567
- }
22568
- },
22569
- "description": "Run constraint validation on all controls without showing\nany UI feedback."
22570
- },
22571
- {
22572
- "kind": "method",
22573
- "name": "reportValidity",
22574
- "privacy": "public",
22575
- "return": {
22576
- "type": {
22577
- "text": ""
22578
- }
22579
- },
22580
- "description": "Run constraint validation, display error messages via each\ncontrol's `esp-form-item`, and scroll the first invalid\nitem into view."
22581
- },
22582
- {
22583
- "kind": "method",
22584
- "name": "reset",
22585
- "privacy": "public",
22586
- "return": {
22587
- "type": {
22588
- "text": "void"
22589
- }
22590
- },
22591
- "description": "Programmatically reset the form and all its controls."
22592
- },
22593
- {
22594
- "kind": "method",
22595
- "name": "submit",
22596
- "privacy": "public",
22597
- "return": {
22598
- "type": {
22599
- "text": "void"
22600
- }
22601
- },
22602
- "description": "Programmatically trigger form submission (with validation)."
22603
- },
22604
- {
22605
- "kind": "method",
22606
- "name": "handleSubmit",
22607
- "privacy": "private",
22608
- "return": {
22609
- "type": {
22610
- "text": "void"
22611
- }
22612
- },
22613
- "parameters": [
22614
- {
22615
- "name": "ev",
22616
- "type": {
22617
- "text": "SubmitEvent"
22618
- }
22619
- }
22620
- ]
22621
- },
22622
- {
22623
- "kind": "method",
22624
- "name": "handleKeyDown",
22625
- "privacy": "private",
22626
- "return": {
22627
- "type": {
22628
- "text": "void"
22629
- }
22630
- },
22631
- "parameters": [
22632
- {
22633
- "name": "ev",
22634
- "type": {
22635
- "text": "KeyboardEvent"
22636
- }
22637
- }
22638
- ]
22639
- },
22640
- {
22641
- "kind": "method",
22642
- "name": "submitViaFetch",
22643
- "privacy": "private",
22644
- "return": {
22645
- "type": {
22646
- "text": "Promise<void>"
22647
- }
22648
- },
22649
- "parameters": [
22650
- {
22651
- "name": "form",
22652
- "type": {
22653
- "text": "HTMLFormElement"
22654
- }
22655
- }
22656
- ]
22657
- },
22658
- {
22659
- "kind": "field",
22660
- "name": "noValidate",
22661
- "type": {
22662
- "text": "boolean"
22663
- },
22664
- "default": "true"
22665
- }
22666
- ],
22667
- "events": [
22668
- {
22669
- "name": "closeDialog",
22670
- "type": {
22671
- "text": "CustomEvent"
22672
- },
22673
- "description": "Fired when `method=\"dialog\"` is used and the form requests its containing `<esp-dialog>` to close. The event detail is an empty object."
22674
- },
22675
- {
22676
- "name": "esp-submit-response",
22677
- "type": {
22678
- "text": "CustomEvent<{ response: Response; ok: boolean }>"
22679
- },
22680
- "description": "Fired after a successful `fetch` submission."
22681
- },
22682
- {
22683
- "name": "esp-submit-error",
22684
- "type": {
22685
- "text": "CustomEvent<{ error: unknown }>"
22686
- },
22687
- "description": "Fired when a `fetch` submission fails."
22688
- },
22689
- {
22690
- "type": {
22691
- "text": "CustomEvent<{ formData: FormData; form: HTMLFormElement }>"
22692
- },
22693
- "description": "Fired when `use-fetch` is true and the form passes validation. Cancelable; calling `preventDefault()` aborts the fetch.",
22694
- "name": "esp-submit"
22695
- }
22696
- ],
22697
- "attributes": [
22698
- {
22699
- "name": "action",
22700
- "type": {
22701
- "text": "string"
22702
- },
22703
- "default": "\"\"",
22704
- "description": "The URL that processes the form submission. When `use-fetch`\nis true this is the URL `fetch()` sends the request to.",
22705
- "fieldName": "action"
22706
- },
22707
- {
22708
- "name": "method",
22709
- "type": {
22710
- "text": "string"
22711
- },
22712
- "default": "\"post\"",
22713
- "description": "The HTTP method for submission. Standard values are `get`,\n`post`, and `dialog`. When set to `dialog`, submitting the\nform closes the nearest `esp-dialog` ancestor without making\na network request.",
22714
- "fieldName": "method"
22715
- },
22716
- {
22717
- "name": "novalidate",
22718
- "type": {
22719
- "text": "boolean"
22720
- },
22721
- "default": "false",
22722
- "description": "When true, constraint validation is skipped during\nsubmission.",
22723
- "fieldName": "novalidate"
22724
- },
22725
- {
22726
- "name": "use-fetch",
22727
- "type": {
22728
- "text": "boolean"
22729
- },
22730
- "default": "false",
22731
- "description": "When true, the form uses `fetch()` instead of native\nbrowser navigation for submission. An `esp-submit` event\nis fired with the `FormData` in the detail, allowing\nconsumers to cancel or modify the request.",
22732
- "fieldName": "useFetch"
22733
- },
22734
- {
22735
- "name": "use-json",
22736
- "type": {
22737
- "text": "boolean"
22738
- },
22739
- "default": "false",
22740
- "description": "When `use-fetch` is true and this is also true, the request\nbody is serialized as JSON instead of `FormData`.",
22741
- "fieldName": "useJson"
22742
- },
22743
- {
22744
- "name": "enctype",
22745
- "type": {
22746
- "text": "string"
22747
- },
22748
- "default": "\"application/x-www-form-urlencoded\"",
22749
- "description": "The encoding type for form submission. Maps to the native\n`enctype` attribute on the inner `<form>`.",
22750
- "fieldName": "enctype"
22751
- },
22752
- {
22753
- "name": "label",
22754
- "type": {
22755
- "text": "string"
22756
- },
22757
- "default": "\"\"",
22758
- "description": "An accessible label applied as `aria-label` on the inner\n`<form>` element so the form is discoverable as an ARIA\n`form` landmark by screen readers.",
22759
- "fieldName": "label"
22760
- }
22761
- ],
22762
- "superclass": {
22763
- "name": "LitElement",
22764
- "package": "lit"
22765
- },
22766
- "tagName": "esp-form",
22767
- "customElement": true,
22768
- "docPageTitle": {
22769
- "name": "Form",
22770
- "description": ""
22771
- },
22772
- "docUrl": {
22773
- "name": "/components/form",
22774
- "description": ""
22775
- },
22776
- "menuGroup": {
22777
- "name": "Form",
22778
- "description": "Controls"
22779
- },
22780
- "menuLabel": {
22781
- "name": "Form",
22782
- "description": ""
22783
- },
22784
- "menuIcon": {
22785
- "name": "forms",
22786
- "description": ""
22787
- }
22788
- }
22789
- ],
22790
- "exports": [
22791
- {
22792
- "kind": "js",
22793
- "name": "EspalierForm",
22794
- "declaration": {
22795
- "name": "EspalierForm",
22796
- "module": "dist/form/esp-form.js"
22797
- }
22798
- },
22799
- {
22800
- "kind": "custom-element-definition",
22801
- "name": "esp-form",
22802
- "declaration": {
22803
- "name": "EspalierForm",
22804
- "module": "dist/form/esp-form.js"
22805
- }
22806
- }
22807
- ]
22808
- },
22809
22809
  {
22810
22810
  "kind": "javascript-module",
22811
22811
  "path": "dist/grid/esp-grid-column.js",
@@ -55382,7 +55382,7 @@
55382
55382
  }
55383
55383
  }
55384
55384
  ],
55385
- "description": "Parse an OKLCH CSS string into its components.\n\nAccepts:\n- `oklch(0.7 0.125 216)`\n- `oklch(70% 0.125 216)`\n\nReturns `null` if the string is not valid OKLCH."
55385
+ "description": "Parse an OKLCH CSS string into its components.\n\nAccepts the CSS Color 4 grammar for the three components:\n- lightness as a number (`0.7`) or percentage (`70%`), clamped to 0–1\n- chroma as a number (`0.125`) or percentage of the 0.4 reference\n maximum (`31.25%` = 0.125), negative values clamped to 0\n- hue as a number or angle (`deg`, `grad`, `rad`, `turn`), signed and\n wrapped into 0–360\n\nAlpha is not supported. Returns `null` if the string is not valid OKLCH."
55386
55386
  },
55387
55387
  {
55388
55388
  "kind": "function",
@@ -55667,6 +55667,33 @@
55667
55667
  }
55668
55668
  ],
55669
55669
  "description": "Derive a semantic color with automatic APCA contrast\nenforcement against a reference background."
55670
+ },
55671
+ {
55672
+ "kind": "variable",
55673
+ "name": "ACCEPTED_COLOR_FORMS",
55674
+ "type": {
55675
+ "text": "string"
55676
+ },
55677
+ "default": "'\"#rrggbb\", \"#rgb\", \"rgb(…)\", \"hsl(…)\", \"oklch(…)\"'",
55678
+ "description": "The color forms parseCssColor accepts, for validation\nerror messages and documentation."
55679
+ },
55680
+ {
55681
+ "kind": "function",
55682
+ "name": "parseCssColor",
55683
+ "return": {
55684
+ "type": {
55685
+ "text": "OklchColor | null"
55686
+ }
55687
+ },
55688
+ "parameters": [
55689
+ {
55690
+ "name": "colorStr",
55691
+ "type": {
55692
+ "text": "string"
55693
+ }
55694
+ }
55695
+ ],
55696
+ "description": "Parse any accepted CSS color form into OKLCH components.\n\nAccepted forms:\n- `oklch(0.7 0.125 216)` / `oklch(70% 31.25% 0.6turn)` — parsed\n natively via parseOklch, no re-rounding\n- `#rrggbb` / `#rgb`\n- `rgb(120, 72, 106)` / `rgb(120 72 106)` — numbers (with exponents)\n or percentages; the legacy comma syntax requires all channels to be\n the same type, the modern syntax may mix; `rgba()` is accepted as an\n alias of the three-argument form\n- `hsl(338, 25%, 38%)` / `hsl(.94turn 25% 38%)` — hue as a number or\n `deg`/`grad`/`rad`/`turn` angle; the legacy comma syntax requires\n percentage saturation and lightness, the modern syntax also accepts\n bare numbers; `hsla()` is accepted as an alias\n\nCSS comments are equivalent to whitespace in every form, and only\nCSS whitespace (tab, newline, form feed, carriage return, space)\nseparates arguments — JavaScript-only whitespace such as NBSP is\nrejected, matching browsers.\n\nNon-OKLCH forms convert through the forward sRGB → OKLab transform,\nnormalized to display precision (L 3 / C 4 / H 1 decimals, hue 0 when\nachromatic) when that rounding reproduces the identical 8-bit color\nin-gamut, and kept at full precision otherwise. Alpha is rejected in\nevery form — theme colors are opaque.\n\nReturns `null` when the string is not a valid instance of any\naccepted form."
55670
55697
  }
55671
55698
  ],
55672
55699
  "exports": [
@@ -55757,6 +55784,22 @@
55757
55784
  "name": "deriveSemanticWithContrast",
55758
55785
  "module": "dist/shared/color-engine.js"
55759
55786
  }
55787
+ },
55788
+ {
55789
+ "kind": "js",
55790
+ "name": "ACCEPTED_COLOR_FORMS",
55791
+ "declaration": {
55792
+ "name": "ACCEPTED_COLOR_FORMS",
55793
+ "module": "dist/shared/color-engine.js"
55794
+ }
55795
+ },
55796
+ {
55797
+ "kind": "js",
55798
+ "name": "parseCssColor",
55799
+ "declaration": {
55800
+ "name": "parseCssColor",
55801
+ "module": "dist/shared/color-engine.js"
55802
+ }
55760
55803
  }
55761
55804
  ]
55762
55805
  },
@@ -1 +1 @@
1
- import{parseOklch as y,serializeOklch as p,gamutMapToSRGB as S,deriveSemantic as v,deriveSemanticWithContrast as B}from"../../shared/color-engine.js";import{generateScaleProperties as x}from"../../shared/scale-engine.js";import{SEMANTIC_COLOR_NAMES as l,LIGHTNESS_KEYS as C,semanticToCSS as b}from"../../shared/theme.js";import{lightnessKeyToCSS as I}from"./lightness-key-to-css.js";import{computeVariants as O}from"./compute-variants.js";function M(o){return{rootFontSize:o.rootFontSize,typeRatio:o.typeRatio,spaceRatio:o.spaceRatio,borderRadius:o.borderRadius,viewportMin:o.viewportMin,viewportMax:o.viewportMax}}function W(o,k){const d=y(o.seedColor);if(!d)return{};const n={};n["--esp-scheme"]=k,n["--esp-seed-color"]=o.seedColor;const c=O(d,o);for(const[a,t]of Object.entries(c))n[`--esp-color-${a}`]=p(S(t));for(const a of C)n[I(a)]=`${Math.round(o.lightness[a]*100)}%`;const u={text:{bg:"background",targetLc:75},dangerText:{bg:"background",targetLc:75},headings:{bg:"background",targetLc:60},headingsHover:{bg:"background",targetLc:60},link:{bg:"background",targetLc:75},linkHover:{bg:"background",targetLc:75},actionText:{bg:"actionBackground",targetLc:75},inputCaret:{bg:"layer2",targetLc:60},inputSelection:{bg:"inputSelectionBg",targetLc:60}},s={};for(const a of l){if(u[a])continue;const t=o.semanticMappings[a],i=o.chroma[a],r=c[t.source],e=o.lightness[t.lightness],g=v(r,e,i.min,i.max);s[a]=g,n[b(a)]=p(g)}for(const a of l){const t=u[a];if(!t)continue;const i=o.semanticMappings[a],r=o.chroma[a],e=c[i.source],g=o.lightness[i.lightness],m=s[t.bg],f=B(e,g,r.min,r.max,m,t.targetLc);s[a]=f,n[b(a)]=p(f)}return Object.assign(n,x(M(o))),o.fontBody&&(n["--esp-font-body"]=o.fontBody),o.fontHeadings&&(n["--esp-font-headings"]=o.fontHeadings),o.fontBrand&&(n["--esp-font-brand"]=o.fontBrand),o.fontMonospace&&(n["--esp-font-monospace"]=o.fontMonospace),n["--esp-font-weight-body"]=o.fontWeightBody,n["--esp-font-weight-headings"]=o.fontWeightHeadings,n["--esp-font-weight-brand"]=o.fontWeightBrand,n["--esp-font-weight-monospace"]=o.fontWeightMonospace,o.pageBackgroundImage&&(n["--esp-page-background-image"]=o.pageBackgroundImage),o.pageBackgroundImageOpacity!==void 0&&(n["--esp-page-background-image-opacity"]=o.pageBackgroundImageOpacity.toString()),o.boxBackgroundImage&&(n["--esp-box-background-image"]=o.boxBackgroundImage),o.boxBackgroundImageOpacity!==void 0&&(n["--esp-box-background-image-opacity"]=o.boxBackgroundImageOpacity.toString()),o.vellumOpacity!==void 0&&(n["--esp-vellum-opacity"]=o.vellumOpacity.toString()),o.vellumBackgroundImage&&(n["--esp-vellum-background-image"]=o.vellumBackgroundImage),o.vellumBackgroundImageOpacity!==void 0&&(n["--esp-vellum-background-image-opacity"]=o.vellumBackgroundImageOpacity.toString()),n}export{W as computeThemeProperties};
1
+ import{parseCssColor as y,serializeOklch as p,gamutMapToSRGB as S,deriveSemantic as v,deriveSemanticWithContrast as B}from"../../shared/color-engine.js";import{generateScaleProperties as C}from"../../shared/scale-engine.js";import{SEMANTIC_COLOR_NAMES as l,LIGHTNESS_KEYS as x,semanticToCSS as b}from"../../shared/theme.js";import{lightnessKeyToCSS as I}from"./lightness-key-to-css.js";import{computeVariants as M}from"./compute-variants.js";function O(o){return{rootFontSize:o.rootFontSize,typeRatio:o.typeRatio,spaceRatio:o.spaceRatio,borderRadius:o.borderRadius,viewportMin:o.viewportMin,viewportMax:o.viewportMax}}function W(o,k){const d=y(o.seedColor);if(!d)return{};const n={};n["--esp-scheme"]=k,n["--esp-seed-color"]=o.seedColor;const c=M(d,o);for(const[a,t]of Object.entries(c))n[`--esp-color-${a}`]=p(S(t));for(const a of x)n[I(a)]=`${Math.round(o.lightness[a]*100)}%`;const u={text:{bg:"background",targetLc:75},dangerText:{bg:"background",targetLc:75},headings:{bg:"background",targetLc:60},headingsHover:{bg:"background",targetLc:60},link:{bg:"background",targetLc:75},linkHover:{bg:"background",targetLc:75},actionText:{bg:"actionBackground",targetLc:75},inputCaret:{bg:"layer2",targetLc:60},inputSelection:{bg:"inputSelectionBg",targetLc:60}},s={};for(const a of l){if(u[a])continue;const t=o.semanticMappings[a],i=o.chroma[a],r=c[t.source],e=o.lightness[t.lightness],g=v(r,e,i.min,i.max);s[a]=g,n[b(a)]=p(g)}for(const a of l){const t=u[a];if(!t)continue;const i=o.semanticMappings[a],r=o.chroma[a],e=c[i.source],g=o.lightness[i.lightness],m=s[t.bg],f=B(e,g,r.min,r.max,m,t.targetLc);s[a]=f,n[b(a)]=p(f)}return Object.assign(n,C(O(o))),o.fontBody&&(n["--esp-font-body"]=o.fontBody),o.fontHeadings&&(n["--esp-font-headings"]=o.fontHeadings),o.fontBrand&&(n["--esp-font-brand"]=o.fontBrand),o.fontMonospace&&(n["--esp-font-monospace"]=o.fontMonospace),n["--esp-font-weight-body"]=o.fontWeightBody,n["--esp-font-weight-headings"]=o.fontWeightHeadings,n["--esp-font-weight-brand"]=o.fontWeightBrand,n["--esp-font-weight-monospace"]=o.fontWeightMonospace,o.pageBackgroundImage&&(n["--esp-page-background-image"]=o.pageBackgroundImage),o.pageBackgroundImageOpacity!==void 0&&(n["--esp-page-background-image-opacity"]=o.pageBackgroundImageOpacity.toString()),o.boxBackgroundImage&&(n["--esp-box-background-image"]=o.boxBackgroundImage),o.boxBackgroundImageOpacity!==void 0&&(n["--esp-box-background-image-opacity"]=o.boxBackgroundImageOpacity.toString()),o.vellumOpacity!==void 0&&(n["--esp-vellum-opacity"]=o.vellumOpacity.toString()),o.vellumBackgroundImage&&(n["--esp-vellum-background-image"]=o.vellumBackgroundImage),o.vellumBackgroundImageOpacity!==void 0&&(n["--esp-vellum-background-image-opacity"]=o.vellumBackgroundImageOpacity.toString()),n}export{W as computeThemeProperties};
@@ -1 +1 @@
1
- const S=Math.PI/180,s=1e-4,w=50;function P(t){const o=t.h*S;return{L:t.l,a:t.c*Math.cos(o),b:t.c*Math.sin(o)}}function k(t){const o=t.L+.3963377774*t.a+.2158037573*t.b,e=t.L-.1055613458*t.a-.0638541728*t.b,n=t.L-.0894841775*t.a-1.291485548*t.b,r=o*o*o,c=e*e*e,a=n*n*n;return{r:4.0767416621*r-3.3077115913*c+.2309699292*a,g:-1.2684380046*r+2.6097574011*c-.3413193965*a,b:-.0041960863*r-.7034186147*c+1.707614701*a}}function p(t){return t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055}function N(t){return{r:p(t.r),g:p(t.g),b:p(t.b)}}function O(t){return t.r>=-s&&t.r<=1+s&&t.g>=-s&&t.g<=1+s&&t.b>=-s&&t.b<=1+s}function M(t){return Math.max(0,Math.min(1,t))}function X(t){const e=t.trim().match(/^oklch\(\s*([\d.]+)(%?)\s+([\d.]+)\s+([\d.]+)\s*\)$/i);if(!e)return null;let n=parseFloat(e[1]);e[2]==="%"&&(n/=100);const r=parseFloat(e[3]),c=parseFloat(e[4]);return isNaN(n)||isNaN(r)||isNaN(c)?null:{l:n,c:r,h:(c%360+360)%360}}function $(t){const o=T(t.l,6),e=T(t.c,6),n=T((t.h%360+360)%360,4);return`oklch(${o} ${e} ${n})`}function m(t){return N(k(P(t)))}function _(t){return O(m(t))}function h(t){if(t.c<=s||_(t))return{...t};let o=0,e=t.c,n={...t};for(let r=0;r<w;r++){const c=(o+e)/2;if(n={l:t.l,c,h:t.h},_(n)?o=c:e=c,e-o<s)break}return{l:t.l,c:o,h:t.h}}function E(t,o,e){const n={l:t.l,c:Math.max(o,Math.min(e,t.c)),h:t.h};return h(n)}const f=.022,G=.012,x=.022,C=.57,L=.56,R=1.14,A=.001,l=.027;function d(t){const o=Math.pow(M(t.r),2.4),e=Math.pow(M(t.g),2.4),n=Math.pow(M(t.b),2.4);return .2126729*o+.7151522*e+.072175*n}function B(t,o){const e=m(h(t)),n=m(h(o));let r=d(e),c=d(n);if(r=r>f?r:r+Math.pow(f-r,1.414),c=c>f?c:c+Math.pow(f-c,1.414),Math.abs(c-r)<A)return 0;let a;if(c>r){const i=Math.pow(c,L),u=Math.pow(r,C);if(a=(i-u)*R,a<A)return 0;a=a<l?a-a*x*l:a-x}else{const i=Math.pow(c,L),u=Math.pow(r,C);if(a=(i-u)*R,a>-A)return 0;a=a>-l?a-a*G*l:a+G}return a*100}function F(t,o,e=60){let n=h({...t}),r=Math.abs(B(n,o));if(r>=e)return n;const c=o.l>.5,a=.01,i=c?0:1;for(let u=0;u<100;u++){if(c?n.l=Math.max(i,n.l-a):n.l=Math.min(i,n.l+a),n=h(n),r=Math.abs(B(n,o)),r>=e)return n;if(n.l<=0||n.l>=1)break}return n}function b(t,o){return((t+o)%360+360)%360}function I(t,o,e,n){return E({l:o,c:t.c,h:t.h},e,n)}function W(t,o,e,n,r,c=60){const a=I(t,o,e,n);return F(a,r,c)}function T(t,o){const e=Math.pow(10,o);return Math.round(t*e)/e}export{B as apcaContrast,E as clampChromaAndGamutMap,I as deriveSemantic,W as deriveSemanticWithContrast,F as ensureContrast,h as gamutMapToSRGB,_ as isInSRGBGamut,m as oklchToSRGB,X as parseOklch,b as rotateHue,$ as serializeOklch};
1
+ const S=Math.PI/180,h=1e-4,$=50,m="[+-]?(?:\\d+|\\d*\\.\\d+)(?:[eE][+-]?\\d+)?",i="[\\t\\n\\f\\r ]",X=new RegExp(i+"*,"+i+"*"),y=new RegExp(i+"+");function G(t){return t.replace(/\/\*[\s\S]*?(?:\*\/|$)/g," ").replace(/^[\t\n\f\r ]+|[\t\n\f\r ]+$/g,"")}const L={"":1,deg:1,grad:.9,rad:180/Math.PI,turn:360},H=.4,U=new RegExp("^oklch\\("+i+"*("+m+")(%?)"+i+"+("+m+")(%?)"+i+"+("+m+")(deg|grad|rad|turn)?"+i+"*\\)$","i");function V(t){const n=t.h*S;return{L:t.l,a:t.c*Math.cos(n),b:t.c*Math.sin(n)}}function W(t){const n=t.L+.3963377774*t.a+.2158037573*t.b,r=t.L-.1055613458*t.a-.0638541728*t.b,e=t.L-.0894841775*t.a-1.291485548*t.b,o=n*n*n,u=r*r*r,c=e*e*e;return{r:4.0767416621*o-3.3077115913*u+.2309699292*c,g:-1.2684380046*o+2.6097574011*u-.3413193965*c,b:-.0041960863*o-.7034186147*u+1.707614701*c}}function T(t){return t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055}function z(t){return{r:T(t.r),g:T(t.g),b:T(t.b)}}function Y(t){return t.r>=-h&&t.r<=1+h&&t.g>=-h&&t.g<=1+h&&t.b>=-h&&t.b<=1+h}function g(t){return Math.max(0,Math.min(1,t))}function q(t){const n=G(t).match(U);if(!n)return null;let r=parseFloat(n[1]);n[2]==="%"&&(r/=100);let e=parseFloat(n[3]);n[4]==="%"&&(e=e/100*H);const o=parseFloat(n[5])*L[(n[6]??"").toLowerCase()];return!isFinite(r)||!isFinite(e)||!isFinite(o)?null:{l:Math.max(0,Math.min(1,r)),c:Math.max(0,e),h:(o%360+360)%360}}function ct(t){const n=p(t.l,6),r=p(t.c,6),e=p((t.h%360+360)%360,4);return`oklch(${n} ${r} ${e})`}function _(t){return z(W(V(t)))}function x(t){return Y(_(t))}function M(t){if(t.c<=h||x(t))return{...t};let n=0,r=t.c,e={...t};for(let o=0;o<$;o++){const u=(n+r)/2;if(e={l:t.l,c:u,h:t.h},x(e)?n=u:r=u,r-n<h)break}return{l:t.l,c:n,h:t.h}}function K(t,n,r){const e={l:t.l,c:Math.max(n,Math.min(r,t.c)),h:t.h};return M(e)}const C=.022,w=.012,O=.022,b=.57,k=.56,P=1.14,A=.001,R=.027;function B(t){const n=Math.pow(g(t.r),2.4),r=Math.pow(g(t.g),2.4),e=Math.pow(g(t.b),2.4);return .2126729*n+.7151522*r+.072175*e}function I(t,n){const r=_(M(t)),e=_(M(n));let o=B(r),u=B(e);if(o=o>C?o:o+Math.pow(C-o,1.414),u=u>C?u:u+Math.pow(C-u,1.414),Math.abs(u-o)<A)return 0;let c;if(u>o){const s=Math.pow(u,k),a=Math.pow(o,b);if(c=(s-a)*P,c<A)return 0;c=c<R?c-c*O*R:c-O}else{const s=Math.pow(u,k),a=Math.pow(o,b);if(c=(s-a)*P,c>-A)return 0;c=c>-R?c-c*w*R:c+w}return c*100}function j(t,n,r=60){let e=M({...t}),o=Math.abs(I(e,n));if(o>=r)return e;const u=n.l>.5,c=.01,s=u?0:1;for(let a=0;a<100;a++){if(u?e.l=Math.max(s,e.l-c):e.l=Math.min(s,e.l+c),e=M(e),o=Math.abs(I(e,n)),o>=r)return e;if(e.l<=0||e.l>=1)break}return e}function st(t,n){return((t+n)%360+360)%360}function J(t,n,r,e){return K({l:n,c:t.c,h:t.h},r,e)}function at(t,n,r,e,o,u=60){const c=J(t,n,r,e);return j(c,o,u)}function E(t){return t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Q(t){const n=E(t.r),r=E(t.g),e=E(t.b),o=Math.cbrt(.4122214708*n+.5363325363*r+.0514459929*e),u=Math.cbrt(.2119034982*n+.6806995451*r+.1073969566*e),c=Math.cbrt(.0883024619*n+.2817188376*r+.6299787005*e);return{L:.2104542553*o+.793617785*u-.0040720468*c,a:1.9779984951*o-2.428592205*u+.4505937099*c,b:.0259040371*o+.7827717662*u-.808675766*c}}function Z(t){const n=Math.sqrt(t.a*t.a+t.b*t.b),r=(Math.atan2(t.b,t.a)/S%360+360)%360;return{l:t.L,c:n,h:r}}function tt(t){const n=p(t.c,4);return{l:p(t.l,3),c:n,h:n===0?0:p(t.h,1)}}function f(t){return Math.round(g(t)*255)}function d(t){const n=Z(Q(t)),r=t.r===t.g&&t.g===t.b?{l:n.l,c:0,h:0}:n,e=tt(r);if(x(e)){const o=_(e);if(f(o.r)===f(t.r)&&f(o.g)===f(t.g)&&f(o.b)===f(t.b))return e}return r}function nt(t){const n=t.slice(1);if(!/^[0-9a-f]+$/i.test(n))return null;if(n.length===3){const[r,e,o]=n.split("").map(u=>parseInt(u+u,16));return{r:r/255,g:e/255,b:o/255}}return n.length===6?{r:parseInt(n.slice(0,2),16)/255,g:parseInt(n.slice(2,4),16)/255,b:parseInt(n.slice(4,6),16)/255}:null}function F(t,n){const r=t.match(new RegExp("^"+n+"\\("+i+"*([^)]*?)"+i+"*\\)$","i"));if(!r)return null;const e=r[1];if(e.includes("/"))return null;const o=e.includes(","),u=e.split(o?X:y);return u.length!==3||u.some(c=>c.length===0)?null:{args:u,legacy:o}}const rt=new RegExp("^("+m+")([a-z%]*)$","i");function D(t){const n=t.match(rt);if(!n)return null;const r=parseFloat(n[1]);return isFinite(r)?{value:r,unit:n[2].toLowerCase()}:null}function et(t){if(t.unit!==""&&t.unit!=="%")return null;const n=t.unit==="%"?t.value/100:t.value/255;return Math.max(0,Math.min(1,n))}function ot(t){const n=L[t.unit];return n===void 0?null:t.value*n}function N(t){return t.unit!==""&&t.unit!=="%"?null:Math.max(0,Math.min(1,t.value/100))}function ut(t,n,r){const e=(t%360+360)%360,o=u=>{const c=(u+e/30)%12,s=n*Math.min(r,1-r);return r-s*Math.max(-1,Math.min(c-3,9-c,1))};return{r:o(0),g:o(8),b:o(4)}}const it='"#rrggbb", "#rgb", "rgb(\u2026)", "hsl(\u2026)", "oklch(\u2026)"';function lt(t){const n=G(t);if(n.startsWith("#")){const r=nt(n);return r?d(r):null}if(/^oklch\(/i.test(n))return q(n);if(/^rgba?\(/i.test(n)){const r=F(n,"rgba?");if(!r)return null;const e=r.args.map(D);if(e.some(l=>l===null))return null;const o=e;if(r.legacy&&new Set(o.map(l=>l.unit)).size>1)return null;const u=o.map(et);if(u.some(l=>l===null))return null;const[c,s,a]=u;return d({r:c,g:s,b:a})}if(/^hsla?\(/i.test(n)){const r=F(n,"hsla?");if(!r)return null;const e=r.args.map(D);if(e.some(v=>v===null))return null;const[o,u,c]=e;if(r.legacy&&(u.unit!=="%"||c.unit!=="%"))return null;const s=ot(o),a=N(u),l=N(c);return s===null||a===null||l===null?null:d(ut(s,a,l))}return null}function p(t,n){const r=Math.pow(10,n);return Math.round(t*r)/r}export{it as ACCEPTED_COLOR_FORMS,I as apcaContrast,K as clampChromaAndGamutMap,J as deriveSemantic,at as deriveSemanticWithContrast,j as ensureContrast,M as gamutMapToSRGB,x as isInSRGBGamut,_ as oklchToSRGB,lt as parseCssColor,q as parseOklch,st as rotateHue,ct as serializeOklch};
@@ -1,4 +1,4 @@
1
- var p=function(f,e,t,o){var r=arguments.length,s=r<3?e:o===null?o=Object.getOwnPropertyDescriptor(e,t):o,a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")s=Reflect.decorate(f,e,t,o);else for(var c=f.length-1;c>=0;c--)(a=f[c])&&(s=(r<3?a(s):r>3?a(e,t,s):a(e,t))||s);return r>3&&s&&Object.defineProperty(e,t,s),s};import{css as E,LitElement as x}from"lit";import{property as y,state as U}from"lit/decorators.js";import{subscribeToRootEvent as g}from"./root-event-subscription.js";import{traverseToClosest as V}from"./utilities.js";import{parseOklch as A,serializeOklch as k,gamutMapToSRGB as O,deriveSemantic as L,deriveSemanticWithContrast as F}from"./color-engine.js";import{computeVariants as T}from"../root/helpers/compute-variants.js";import{SEMANTIC_COLOR_NAMES as S,semanticToCSS as P}from"./theme.js";import{alignAttributeTextInheritance as _,focusRing as z}from"./style-fragments.js";class n extends x{constructor(){super(...arguments),this.seedColorBacker="oklch(0.7 0.125 216)",this.espRoot=null,this.variantTokenOriginalValues=new Map,this.rootEventSubscriptionsActive=!1,this.subscribedRoot=null,this.rootEventUnsubscribers=[],this.variantBacker="primary",this.correlationId=globalThis.crypto?.randomUUID?.()??Math.random().toString(36),this.scheme="light",this.handleSeedColorChanged=e=>{this.syncRootFromDom(!1)&&(this.seedColor=e.seedColor,this.applyVariantTokens())},this.handleSchemeChanged=e=>{!this.syncRootFromDom(!1)||this.scheme===e.scheme||(this.scheme=e.scheme,this.applyVariantTokens())},this.handleThemeChanged=()=>{this.syncRootFromDom(!1)&&this.applyVariantTokens()},this.handleIconSpriteUrlChanged=()=>{this.syncRootFromDom(!1)&&this.requestUpdate()}}get seedColor(){return this.seedColorBacker}set seedColor(e){this.seedColorBacker=e}focusResolvedElementAfterUpdate(e,t){const o=()=>{const r=e();return r?(r.focus(t),!0):!1};o()||this.updateComplete.then(()=>{o()})}focusShadowElementAfterUpdate(e,t){this.focusResolvedElementAfterUpdate(()=>this.shadowRoot?.querySelector(e),t)}emitValueChanged(e){this.dispatchEvent(new CustomEvent("value-changed",{detail:e,bubbles:!0,composed:!0}))}get variant(){return this.variantBacker}set variant(e){this.variantBacker=e,this.applyVariantTokens()}connectedCallback(){super.connectedCallback();const e=this.syncRootFromDom(!1);e&&this.subscribeToRootEvents(e)}disconnectedCallback(){super.disconnectedCallback(),this.unsubscribeFromRootEvents()}firstUpdated(e){this.syncRootFromDom(!0)}syncRootFromDom(e){const t=V(this,"esp-root");if(!t){if(this.espRoot&&this.clearVariantTokens(),this.unsubscribeFromRootEvents(),this.espRoot=null,!e)return null;throw new Error("No esp-root ancestor found. Espalier components must be placed inside an <esp-root> element.")}const o=t!==this.espRoot,s=t.scheme==="dark"?"dark":"light",a=this.scheme!==s,c=this.seedColor!==t.seedColor;return this.espRoot=t,o&&this.isConnected&&this.subscribeToRootEvents(t),(o||a||c)&&(this.scheme=s,this.seedColor=t.seedColor,this.applyVariantTokens()),o&&this.requestUpdate(),t}subscribeToRootEvents(e){this.rootEventSubscriptionsActive&&this.subscribedRoot===e||(this.unsubscribeFromRootEvents(),this.subscribedRoot=e,this.rootEventUnsubscribers=[g(e,"seed-color-changed",this.handleSeedColorChanged),g(e,"scheme-changed",this.handleSchemeChanged),g(e,"theme-changed",this.handleThemeChanged),g(e,"icon-sprite-url-changed",this.handleIconSpriteUrlChanged)],this.rootEventSubscriptionsActive=!0)}unsubscribeFromRootEvents(){if(this.rootEventSubscriptionsActive){for(const e of this.rootEventUnsubscribers)e();this.rootEventUnsubscribers=[],this.subscribedRoot=null,this.rootEventSubscriptionsActive=!1}}traverseToClosest(e){return V(this,e)}applyVariantTokens(){if(!this.espRoot)return;const e=this.getVariantColorSource();if(!e){this.clearVariantTokens();return}const t=this.espRoot.activeTheme,o=A(t.seedColor);if(!o)return;const s=T(o,t)[e];if(!s){this.clearVariantTokens();return}this.setVariantTokenProperty("--esp-color-primary",k(O(s)));const a=T(s,t),c=n.statusVariantSources.has(e),{textContrast:C}=n,b={};for(const i of S){if(C[i])continue;const l=t.semanticMappings[i],h=t.chroma[i],d=this.effectiveVariantTokenSource(i,l.source,c),v=a[d],m=t.lightness[l.lightness],u=L(v,m,h.min,h.max);b[i]=u,this.setVariantTokenProperty(P(i),k(u))}for(const i of S){const l=C[i];if(!l)continue;const h=t.semanticMappings[i],d=t.chroma[i],v=this.effectiveVariantTokenSource(i,h.source,c),m=a[v],u=t.lightness[h.lightness],w=b[l.bg],R=F(m,u,d.min,d.max,w,l.targetLc);b[i]=R,this.setVariantTokenProperty(P(i),k(R))}}effectiveVariantTokenSource(e,t,o){return o&&n.semanticActionTokens.has(e)?"primary":t}getVariantColorSource(){switch(this.variant){case"":case"primary":case"neutral":return"";case"info":return"complementary";default:return this.variant}}setVariantTokenProperty(e,t){const o=this.variantTokenOriginalValues.get(e);if(o){const r=this.style.getPropertyValue(e),s=this.style.getPropertyPriority(e);(r!==o.generatedValue||s!==o.generatedPriority)&&(o.originalValue=r.length?r:null,o.originalPriority=s),o.generatedValue=t,o.generatedPriority=""}else{const r=this.style.getPropertyValue(e);this.variantTokenOriginalValues.set(e,{generatedPriority:"",generatedValue:t,originalPriority:this.style.getPropertyPriority(e),originalValue:r.length?r:null})}this.style.setProperty(e,t)}clearVariantTokens(){for(const[e,t]of this.variantTokenOriginalValues){const o=this.style.getPropertyValue(e),r=this.style.getPropertyPriority(e);(o!==t.generatedValue||r!==t.generatedPriority)&&(t.originalValue=o.length?o:null,t.originalPriority=r),t.originalValue===null?this.style.removeProperty(e):this.style.setProperty(e,t.originalValue,t.originalPriority)}this.variantTokenOriginalValues.clear()}}n.textContrast={text:{bg:"background",targetLc:75},dangerText:{bg:"background",targetLc:75},headings:{bg:"background",targetLc:60},headingsHover:{bg:"background",targetLc:60},link:{bg:"background",targetLc:75},linkHover:{bg:"background",targetLc:75},actionText:{bg:"actionBackground",targetLc:75},inputCaret:{bg:"layer2",targetLc:60},inputSelection:{bg:"inputSelectionBg",targetLc:60}},n.statusVariantSources=new Set(["danger","success","warning"]),n.semanticActionTokens=new Set(["actionBackground","actionText"]),n.styles=[z(".esp-field:focus-within","--esp-field-focus-shadow"),_,E`
1
+ var p=function(f,e,t,o){var r=arguments.length,s=r<3?e:o===null?o=Object.getOwnPropertyDescriptor(e,t):o,a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")s=Reflect.decorate(f,e,t,o);else for(var c=f.length-1;c>=0;c--)(a=f[c])&&(s=(r<3?a(s):r>3?a(e,t,s):a(e,t))||s);return r>3&&s&&Object.defineProperty(e,t,s),s};import{css as E,LitElement as x}from"lit";import{property as y,state as U}from"lit/decorators.js";import{subscribeToRootEvent as g}from"./root-event-subscription.js";import{traverseToClosest as V}from"./utilities.js";import{parseCssColor as A,serializeOklch as k,gamutMapToSRGB as L,deriveSemantic as O,deriveSemanticWithContrast as F}from"./color-engine.js";import{computeVariants as T}from"../root/helpers/compute-variants.js";import{SEMANTIC_COLOR_NAMES as S,semanticToCSS as P}from"./theme.js";import{alignAttributeTextInheritance as _,focusRing as z}from"./style-fragments.js";class n extends x{constructor(){super(...arguments),this.seedColorBacker="oklch(0.7 0.125 216)",this.espRoot=null,this.variantTokenOriginalValues=new Map,this.rootEventSubscriptionsActive=!1,this.subscribedRoot=null,this.rootEventUnsubscribers=[],this.variantBacker="primary",this.correlationId=globalThis.crypto?.randomUUID?.()??Math.random().toString(36),this.scheme="light",this.handleSeedColorChanged=e=>{this.syncRootFromDom(!1)&&(this.seedColor=e.seedColor,this.applyVariantTokens())},this.handleSchemeChanged=e=>{!this.syncRootFromDom(!1)||this.scheme===e.scheme||(this.scheme=e.scheme,this.applyVariantTokens())},this.handleThemeChanged=()=>{this.syncRootFromDom(!1)&&this.applyVariantTokens()},this.handleIconSpriteUrlChanged=()=>{this.syncRootFromDom(!1)&&this.requestUpdate()}}get seedColor(){return this.seedColorBacker}set seedColor(e){this.seedColorBacker=e}focusResolvedElementAfterUpdate(e,t){const o=()=>{const r=e();return r?(r.focus(t),!0):!1};o()||this.updateComplete.then(()=>{o()})}focusShadowElementAfterUpdate(e,t){this.focusResolvedElementAfterUpdate(()=>this.shadowRoot?.querySelector(e),t)}emitValueChanged(e){this.dispatchEvent(new CustomEvent("value-changed",{detail:e,bubbles:!0,composed:!0}))}get variant(){return this.variantBacker}set variant(e){this.variantBacker=e,this.applyVariantTokens()}connectedCallback(){super.connectedCallback();const e=this.syncRootFromDom(!1);e&&this.subscribeToRootEvents(e)}disconnectedCallback(){super.disconnectedCallback(),this.unsubscribeFromRootEvents()}firstUpdated(e){this.syncRootFromDom(!0)}syncRootFromDom(e){const t=V(this,"esp-root");if(!t){if(this.espRoot&&this.clearVariantTokens(),this.unsubscribeFromRootEvents(),this.espRoot=null,!e)return null;throw new Error("No esp-root ancestor found. Espalier components must be placed inside an <esp-root> element.")}const o=t!==this.espRoot,s=t.scheme==="dark"?"dark":"light",a=this.scheme!==s,c=this.seedColor!==t.seedColor;return this.espRoot=t,o&&this.isConnected&&this.subscribeToRootEvents(t),(o||a||c)&&(this.scheme=s,this.seedColor=t.seedColor,this.applyVariantTokens()),o&&this.requestUpdate(),t}subscribeToRootEvents(e){this.rootEventSubscriptionsActive&&this.subscribedRoot===e||(this.unsubscribeFromRootEvents(),this.subscribedRoot=e,this.rootEventUnsubscribers=[g(e,"seed-color-changed",this.handleSeedColorChanged),g(e,"scheme-changed",this.handleSchemeChanged),g(e,"theme-changed",this.handleThemeChanged),g(e,"icon-sprite-url-changed",this.handleIconSpriteUrlChanged)],this.rootEventSubscriptionsActive=!0)}unsubscribeFromRootEvents(){if(this.rootEventSubscriptionsActive){for(const e of this.rootEventUnsubscribers)e();this.rootEventUnsubscribers=[],this.subscribedRoot=null,this.rootEventSubscriptionsActive=!1}}traverseToClosest(e){return V(this,e)}applyVariantTokens(){if(!this.espRoot)return;const e=this.getVariantColorSource();if(!e){this.clearVariantTokens();return}const t=this.espRoot.activeTheme,o=A(t.seedColor);if(!o)return;const s=T(o,t)[e];if(!s){this.clearVariantTokens();return}this.setVariantTokenProperty("--esp-color-primary",k(L(s)));const a=T(s,t),c=n.statusVariantSources.has(e),{textContrast:C}=n,b={};for(const i of S){if(C[i])continue;const l=t.semanticMappings[i],h=t.chroma[i],d=this.effectiveVariantTokenSource(i,l.source,c),v=a[d],m=t.lightness[l.lightness],u=O(v,m,h.min,h.max);b[i]=u,this.setVariantTokenProperty(P(i),k(u))}for(const i of S){const l=C[i];if(!l)continue;const h=t.semanticMappings[i],d=t.chroma[i],v=this.effectiveVariantTokenSource(i,h.source,c),m=a[v],u=t.lightness[h.lightness],w=b[l.bg],R=F(m,u,d.min,d.max,w,l.targetLc);b[i]=R,this.setVariantTokenProperty(P(i),k(R))}}effectiveVariantTokenSource(e,t,o){return o&&n.semanticActionTokens.has(e)?"primary":t}getVariantColorSource(){switch(this.variant){case"":case"primary":case"neutral":return"";case"info":return"complementary";default:return this.variant}}setVariantTokenProperty(e,t){const o=this.variantTokenOriginalValues.get(e);if(o){const r=this.style.getPropertyValue(e),s=this.style.getPropertyPriority(e);(r!==o.generatedValue||s!==o.generatedPriority)&&(o.originalValue=r.length?r:null,o.originalPriority=s),o.generatedValue=t,o.generatedPriority=""}else{const r=this.style.getPropertyValue(e);this.variantTokenOriginalValues.set(e,{generatedPriority:"",generatedValue:t,originalPriority:this.style.getPropertyPriority(e),originalValue:r.length?r:null})}this.style.setProperty(e,t)}clearVariantTokens(){for(const[e,t]of this.variantTokenOriginalValues){const o=this.style.getPropertyValue(e),r=this.style.getPropertyPriority(e);(o!==t.generatedValue||r!==t.generatedPriority)&&(t.originalValue=o.length?o:null,t.originalPriority=r),t.originalValue===null?this.style.removeProperty(e):this.style.setProperty(e,t.originalValue,t.originalPriority)}this.variantTokenOriginalValues.clear()}}n.textContrast={text:{bg:"background",targetLc:75},dangerText:{bg:"background",targetLc:75},headings:{bg:"background",targetLc:60},headingsHover:{bg:"background",targetLc:60},link:{bg:"background",targetLc:75},linkHover:{bg:"background",targetLc:75},actionText:{bg:"actionBackground",targetLc:75},inputCaret:{bg:"layer2",targetLc:60},inputSelection:{bg:"inputSelectionBg",targetLc:60}},n.statusVariantSources=new Set(["danger","success","warning"]),n.semanticActionTokens=new Set(["actionBackground","actionText"]),n.styles=[z(".esp-field:focus-within","--esp-field-focus-shadow"),_,E`
2
2
  :host {
3
3
 
4
4
  --_esp-field-resolved-hover-bg: var(
@@ -74,7 +74,14 @@ export interface SemanticMapping {
74
74
  export type SemanticMappings = Record<SemanticColorName, SemanticMapping>;
75
75
  /** The complete, resolved Espalier theme. */
76
76
  export interface EspalierTheme {
77
- /** OKLCH seed color string — drives the entire palette. */
77
+ /**
78
+ * Seed color — drives the entire palette.
79
+ *
80
+ * A theme author may supply any common CSS color form (`#rrggbb`,
81
+ * `#rgb`, `rgb()`, `hsl()`, or `oklch()`); non-OKLCH forms are
82
+ * converted at the merge boundary, so a **resolved** theme always
83
+ * carries an `oklch()` string here.
84
+ */
78
85
  seedColor: string;
79
86
  /** CSS `font-family` for body / UI text. */
80
87
  fontBody: string;
@@ -1 +1 @@
1
- import{parseOklch as O}from"./color-engine.js";const h=["background","layer1","layer2","layer3","layer4","actionBackground","actionText","border","shadow","text","dangerText","headings","headingsHover","link","linkHover","linkHoverBg","inputCaret","inputSelection","inputSelectionBg"],$=["primary","analogous-left","analogous-right","complementary","split-complementary-left","split-complementary-right","triadic-left","triadic-right","danger","success","warning"],C=["analogous-left","analogous-right","complementary","split-complementary-left","split-complementary-right","triadic-left","triadic-right","danger","success","warning"],d=["surface","raised1","raised2","raised3","raised4","accent","muted","text","border","ink","shadow"],I={surface:.99,raised1:.97,raised2:.9,raised3:.8,raised4:.72,accent:.5,muted:.45,text:.35,border:.3,ink:.25,shadow:.2},T={surface:.15,raised1:.2,raised2:.3,raised3:.4,raised4:.36,accent:.6,muted:.75,text:.85,border:.7,ink:.95,shadow:.6},y={background:{source:"primary",lightness:"surface"},layer1:{source:"primary",lightness:"raised1"},layer2:{source:"primary",lightness:"raised2"},layer3:{source:"primary",lightness:"raised3"},layer4:{source:"primary",lightness:"raised4"},actionBackground:{source:"primary",lightness:"raised3"},actionText:{source:"primary",lightness:"ink"},border:{source:"primary",lightness:"border"},shadow:{source:"primary",lightness:"shadow"},text:{source:"primary",lightness:"text"},dangerText:{source:"danger",lightness:"text"},headings:{source:"primary",lightness:"muted"},headingsHover:{source:"primary",lightness:"text"},link:{source:"complementary",lightness:"accent"},linkHover:{source:"complementary",lightness:"text"},linkHoverBg:{source:"triadic-left",lightness:"raised2"},inputCaret:{source:"triadic-right",lightness:"ink"},inputSelection:{source:"complementary",lightness:"text"},inputSelectionBg:{source:"triadic-left",lightness:"raised2"}},f=.4;function b(){const i={};for(const e of h)i[e]={min:0,max:f};return i}const B={seedColor:"oklch(0.7 0.125 216)",fontBody:"",fontHeadings:"",fontBrand:"",fontMonospace:"",fontWeightBody:"normal",fontWeightHeadings:"bold",fontWeightBrand:"bold",fontWeightMonospace:"normal",stylesheets:[],rootFontSize:16,typeRatio:1.25,spaceRatio:1.618,borderRadius:.2,viewportMin:320,viewportMax:1200,angles:{analogous:30,complementary:180,splitComplementary:30,triadic:120},semanticHues:{danger:27,success:150,warning:90},variantChroma:{},chroma:b(),semanticMappings:{...y}},_={...B,chroma:b(),semanticMappings:{...y},lightness:{...I}},W={...B,chroma:b(),semanticMappings:{...y},lightness:{...T}},M=new Set(["__proto__","constructor","prototype"]);function l(i){return!M.has(i)}function w(i,e){return M.has(i)?void 0:e}function F(i){return`--esp-color-${i.replace(/([A-Z])/g,"-$1").replace(/(\d)/g,"-$1").toLowerCase()}`}function R(i){try{const e=atob(i),s=JSON.parse(e,w);return typeof s!="object"||s===null||Array.isArray(s)?null:s}catch{return null}}function k(i){return btoa(JSON.stringify(i))}function N(i,e){const s={...i,seedColor:e.seedColor??i.seedColor,fontBody:e.fontBody??i.fontBody,fontHeadings:e.fontHeadings??i.fontHeadings,fontBrand:e.fontBrand??i.fontBrand,fontMonospace:e.fontMonospace??i.fontMonospace,fontWeightBody:String(e.fontWeightBody??i.fontWeightBody),fontWeightHeadings:String(e.fontWeightHeadings??i.fontWeightHeadings),fontWeightBrand:String(e.fontWeightBrand??i.fontWeightBrand),fontWeightMonospace:String(e.fontWeightMonospace??i.fontWeightMonospace),stylesheets:e.stylesheets??[...i.stylesheets],rootFontSize:e.rootFontSize??i.rootFontSize,typeRatio:e.typeRatio??i.typeRatio,spaceRatio:e.spaceRatio??i.spaceRatio,borderRadius:e.borderRadius??i.borderRadius,viewportMin:e.viewportMin??i.viewportMin,viewportMax:e.viewportMax??i.viewportMax,angles:{...i.angles,...e.angles},semanticHues:{...i.semanticHues,...e.semanticHues},variantChroma:{...i.variantChroma,...e.variantChroma},lightness:{...i.lightness,...e.lightness},chroma:S(i.chroma,e.chroma),semanticMappings:S(i.semanticMappings,e.semanticMappings)};return e.pageBackgroundImage!==void 0&&(s.pageBackgroundImage=e.pageBackgroundImage),e.pageBackgroundImageOpacity!==void 0&&(s.pageBackgroundImageOpacity=e.pageBackgroundImageOpacity),e.boxBackgroundImage!==void 0&&(s.boxBackgroundImage=e.boxBackgroundImage),e.boxBackgroundImageOpacity!==void 0&&(s.boxBackgroundImageOpacity=e.boxBackgroundImageOpacity),e.vellumOpacity!==void 0&&(s.vellumOpacity=e.vellumOpacity),e.vellumBackgroundImage!==void 0&&(s.vellumBackgroundImage=e.vellumBackgroundImage),e.vellumBackgroundImageOpacity!==void 0&&(s.vellumBackgroundImageOpacity=e.vellumBackgroundImageOpacity),s}function S(i,e){if(!e)return{...i};const s={...i};for(const c of Object.keys(e)){const u=e[c];u!==void 0&&(s[c]=u)}return s}function L(i){const e=[],s=[];let c;try{c=atob(i)}catch{return e.push("Failed to decode Base64 string."),{valid:!1,errors:e,warnings:s}}let u;try{u=JSON.parse(c)}catch{return e.push("Decoded string is not valid JSON."),{valid:!1,errors:e,warnings:s}}if(typeof u!="object"||u===null||Array.isArray(u))return e.push("Theme must be a JSON object."),{valid:!1,errors:e,warnings:s};const o=u;"seedColor"in o&&(typeof o.seedColor!="string"?e.push("seedColor must be a string."):O(o.seedColor)||e.push(`seedColor is not a valid OKLCH value: "${o.seedColor}".`));for(const t of["fontBody","fontHeadings","fontBrand","fontMonospace"])t in o&&typeof o[t]!="string"&&e.push(`${t} must be a string.`);const g=["normal","bold","lighter","bolder"],p=["inherit","initial","unset","revert","revert-layer"];for(const t of["fontWeightBody","fontWeightHeadings","fontWeightBrand","fontWeightMonospace"])if(t in o){const n=o[t];if(typeof n=="number")(n<1||n>1e3)&&e.push(`${t} numeric value must be 1\u20131000 (got ${n}).`);else if(typeof n=="string"){const a=g.includes(n)||p.includes(n);if(/^\d+$/.test(n)){const x=Number(n);(x<1||x>1e3)&&e.push(`${t} numeric value must be 1\u20131000 (got "${n}").`)}else a||s.push(`${t} = "${n}" is not a standard font-weight value.`)}else e.push(`${t} must be a string or number.`)}"stylesheets"in o&&(Array.isArray(o.stylesheets)?o.stylesheets.some(t=>typeof t!="string")&&e.push("Every entry in stylesheets must be a string."):e.push("stylesheets must be an array of strings."));const m=[["rootFontSize",1,100],["borderRadius",0,10],["viewportMin",100,1e4],["viewportMax",200,1e4]];for(const[t,n,a]of m)if(t in o)if(typeof o[t]!="number"||!isFinite(o[t]))e.push(`${t} must be a finite number.`);else{const r=o[t];n!==void 0&&r<n&&e.push(`${t} must be \u2265 ${n} (got ${r}).`),a!==void 0&&r>a&&s.push(`${t} = ${r} is unusually high (max ${a}).`)}for(const t of["typeRatio","spaceRatio"])if(t in o)if(typeof o[t]!="number"||!isFinite(o[t]))e.push(`${t} must be a finite number.`);else{const n=o[t];n<=1&&e.push(`${t} must be > 1 (got ${n}).`);const a=t==="typeRatio"?1.3:2;n>a&&s.push(`${t} = ${n} is very high (max ${a}); scales may be extreme.`)}if("viewportMin"in o&&"viewportMax"in o){const t=o.viewportMin,n=o.viewportMax;typeof t=="number"&&typeof n=="number"&&t>=n&&e.push(`viewportMin (${t}) must be less than viewportMax (${n}).`)}if("angles"in o)if(typeof o.angles!="object"||o.angles===null)e.push("angles must be an object.");else{const t=o.angles;for(const n of["analogous","complementary","splitComplementary","triadic"])if(n in t)if(typeof t[n]!="number"||!isFinite(t[n]))e.push(`angles.${n} must be a finite number.`);else{const a=t[n];(a<0||a>360)&&s.push(`angles.${n} = ${a} is outside 0\u2013360.`)}}if("semanticHues"in o)if(typeof o.semanticHues!="object"||o.semanticHues===null)e.push("semanticHues must be an object.");else{const t=o.semanticHues;for(const n of["danger","success","warning"])if(n in t)if(typeof t[n]!="number"||!isFinite(t[n]))e.push(`semanticHues.${n} must be a finite number.`);else{const a=t[n];(a<0||a>360)&&s.push(`semanticHues.${n} = ${a} is outside 0\u2013360.`)}}if("variantChroma"in o)if(typeof o.variantChroma!="object"||o.variantChroma===null)e.push("variantChroma must be an object.");else{const t=o.variantChroma;for(const n of C)if(n in t)if(typeof t[n]!="number"||!isFinite(t[n]))e.push(`variantChroma["${n}"] must be a finite number.`);else{const a=t[n];a<0&&e.push(`variantChroma["${n}"] must be \u2265 0 (got ${a}).`),a>f&&s.push(`variantChroma["${n}"] = ${a} exceeds ${f}.`)}}if("lightness"in o)if(typeof o.lightness!="object"||o.lightness===null)e.push("lightness must be an object.");else{const t=o.lightness;for(const n of d)if(n in t)if(typeof t[n]!="number"||!isFinite(t[n]))e.push(`lightness.${n} must be a finite number.`);else{const a=t[n];(a<0||a>1)&&e.push(`lightness.${n} must be 0\u20131 (got ${a}).`)}}if("chroma"in o)if(typeof o.chroma!="object"||o.chroma===null)e.push("chroma must be an object.");else{const t=o.chroma;for(const n of h)if(n in t){const a=t[n];if(typeof a!="object"||a===null){e.push(`chroma.${n} must be { min, max }.`);continue}const r=a;(typeof r.min!="number"||r.min<0)&&e.push(`chroma.${n}.min must be \u2265 0.`),(typeof r.max!="number"||r.max<0||r.max>f)&&e.push(`chroma.${n}.max must be 0\u2013${f}.`),typeof r.min=="number"&&typeof r.max=="number"&&r.min>r.max&&e.push(`chroma.${n}.min (${r.min}) must be \u2264 max (${r.max}).`)}}if("semanticMappings"in o)if(typeof o.semanticMappings!="object"||o.semanticMappings===null)e.push("semanticMappings must be an object.");else{const t=o.semanticMappings;for(const n of h)if(n in t){const a=t[n];if(typeof a!="object"||a===null){e.push(`semanticMappings.${n} must be { source, lightness }.`);continue}const r=a;(typeof r.source!="string"||!$.includes(r.source))&&e.push(`semanticMappings.${n}.source must be one of: ${$.join(", ")}.`),(typeof r.lightness!="string"||!d.includes(r.lightness))&&e.push(`semanticMappings.${n}.lightness must be one of: ${d.join(", ")}.`)}}for(const t of["pageBackgroundImage","boxBackgroundImage","vellumBackgroundImage"])t in o&&typeof o[t]!="string"&&e.push(`${t} must be a string.`);for(const t of["pageBackgroundImageOpacity","boxBackgroundImageOpacity","vellumOpacity","vellumBackgroundImageOpacity"])if(t in o)if(typeof o[t]!="number"||!isFinite(o[t]))e.push(`${t} must be a finite number.`);else{const n=o[t];(n<0||n>1)&&e.push(`${t} must be 0\u20131 (got ${n}).`)}return{valid:e.length===0,errors:e,warnings:s}}const A=["angles","chroma","lightness","semanticHues","semanticMappings","variantChroma"];function E(i,e){const s={};for(const[c,u]of Object.entries(i))l(c)&&(s[c]=u);for(const[c,u]of Object.entries(e))u!==void 0&&l(c)&&(s[c]=u);for(const c of A){const u=i[c],o=e[c];if(u&&typeof u=="object"&&!Array.isArray(u)&&o&&typeof o=="object"&&!Array.isArray(o)){const g={};for(const[p,m]of Object.entries(u))l(p)&&(g[p]=m);for(const[p,m]of Object.entries(o))m!==void 0&&l(p)&&(g[p]=m);s[c]=g}}return s}function D(...i){let e={};for(const s of i){if(!s)continue;const c=R(s);c&&(e=E(e,c))}return k(e)}function v(i){return i.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")}const H={borderRadius:.1,fontBody:'"Noto Sans", sans-serif',fontWeightBody:"normal",fontHeadings:'"Inika", serif',fontWeightHeadings:"normal",fontBrand:'"Inika", serif',fontWeightBrand:"normal",fontMonospace:'"JetBrains Mono", monospace',fontWeightMonospace:"normal",seedColor:"oklch(.07 0.15 216)"};function K(i){return k({...H,pageBackgroundImage:`url("${v(i)}")`,pageBackgroundImageOpacity:.5})}function U(i){return k({...H,pageBackgroundImage:`url("${v(i)}")`,pageBackgroundImageOpacity:.55})}export{$ as COLOR_SOURCES,T as DEFAULT_DARK_LIGHTNESS,W as DEFAULT_DARK_THEME,I as DEFAULT_LIGHT_LIGHTNESS,_ as DEFAULT_LIGHT_THEME,y as DEFAULT_SEMANTIC_MAPPINGS,d as LIGHTNESS_KEYS,A as NESTED_THEME_KEYS,h as SEMANTIC_COLOR_NAMES,C as VARIANT_COLOR_SOURCES,U as buildTaprootDarkTheme,K as buildTaprootLightTheme,k as encodeTheme,D as layerThemes,E as mergePartials,N as mergeTheme,R as parseTheme,F as semanticToCSS,L as validateTheme};
1
+ import{ACCEPTED_COLOR_FORMS as v,parseCssColor as $,parseOklch as I,serializeOklch as T}from"./color-engine.js";const h=["background","layer1","layer2","layer3","layer4","actionBackground","actionText","border","shadow","text","dangerText","headings","headingsHover","link","linkHover","linkHoverBg","inputCaret","inputSelection","inputSelectionBg"],B=["primary","analogous-left","analogous-right","complementary","split-complementary-left","split-complementary-right","triadic-left","triadic-right","danger","success","warning"],R=["analogous-left","analogous-right","complementary","split-complementary-left","split-complementary-right","triadic-left","triadic-right","danger","success","warning"],d=["surface","raised1","raised2","raised3","raised4","accent","muted","text","border","ink","shadow"],w={surface:.99,raised1:.97,raised2:.9,raised3:.8,raised4:.72,accent:.5,muted:.45,text:.35,border:.3,ink:.25,shadow:.2},A={surface:.15,raised1:.2,raised2:.3,raised3:.4,raised4:.36,accent:.6,muted:.75,text:.85,border:.7,ink:.95,shadow:.6},y={background:{source:"primary",lightness:"surface"},layer1:{source:"primary",lightness:"raised1"},layer2:{source:"primary",lightness:"raised2"},layer3:{source:"primary",lightness:"raised3"},layer4:{source:"primary",lightness:"raised4"},actionBackground:{source:"primary",lightness:"raised3"},actionText:{source:"primary",lightness:"ink"},border:{source:"primary",lightness:"border"},shadow:{source:"primary",lightness:"shadow"},text:{source:"primary",lightness:"text"},dangerText:{source:"danger",lightness:"text"},headings:{source:"primary",lightness:"muted"},headingsHover:{source:"primary",lightness:"text"},link:{source:"complementary",lightness:"accent"},linkHover:{source:"complementary",lightness:"text"},linkHoverBg:{source:"triadic-left",lightness:"raised2"},inputCaret:{source:"triadic-right",lightness:"ink"},inputSelection:{source:"complementary",lightness:"text"},inputSelectionBg:{source:"triadic-left",lightness:"raised2"}},f=.4;function b(){const i={};for(const e of h)i[e]={min:0,max:f};return i}const M={seedColor:"oklch(0.7 0.125 216)",fontBody:"",fontHeadings:"",fontBrand:"",fontMonospace:"",fontWeightBody:"normal",fontWeightHeadings:"bold",fontWeightBrand:"bold",fontWeightMonospace:"normal",stylesheets:[],rootFontSize:16,typeRatio:1.25,spaceRatio:1.618,borderRadius:.2,viewportMin:320,viewportMax:1200,angles:{analogous:30,complementary:180,splitComplementary:30,triadic:120},semanticHues:{danger:27,success:150,warning:90},variantChroma:{},chroma:b(),semanticMappings:{...y}},L={...M,chroma:b(),semanticMappings:{...y},lightness:{...w}},D={...M,chroma:b(),semanticMappings:{...y},lightness:{...A}},S=new Set(["__proto__","constructor","prototype"]);function l(i){return!S.has(i)}function E(i,e){return S.has(i)?void 0:e}function U(i){return`--esp-color-${i.replace(/([A-Z])/g,"-$1").replace(/(\d)/g,"-$1").toLowerCase()}`}function _(i){try{const e=atob(i),s=JSON.parse(e,E);return typeof s!="object"||s===null||Array.isArray(s)?null:s}catch{return null}}function k(i){return btoa(JSON.stringify(i))}function j(i){if(I(i))return i;const e=$(i);return e?T(e):i}function K(i,e){const s={...i,seedColor:typeof e.seedColor=="string"?j(e.seedColor):i.seedColor,fontBody:e.fontBody??i.fontBody,fontHeadings:e.fontHeadings??i.fontHeadings,fontBrand:e.fontBrand??i.fontBrand,fontMonospace:e.fontMonospace??i.fontMonospace,fontWeightBody:String(e.fontWeightBody??i.fontWeightBody),fontWeightHeadings:String(e.fontWeightHeadings??i.fontWeightHeadings),fontWeightBrand:String(e.fontWeightBrand??i.fontWeightBrand),fontWeightMonospace:String(e.fontWeightMonospace??i.fontWeightMonospace),stylesheets:e.stylesheets??[...i.stylesheets],rootFontSize:e.rootFontSize??i.rootFontSize,typeRatio:e.typeRatio??i.typeRatio,spaceRatio:e.spaceRatio??i.spaceRatio,borderRadius:e.borderRadius??i.borderRadius,viewportMin:e.viewportMin??i.viewportMin,viewportMax:e.viewportMax??i.viewportMax,angles:{...i.angles,...e.angles},semanticHues:{...i.semanticHues,...e.semanticHues},variantChroma:{...i.variantChroma,...e.variantChroma},lightness:{...i.lightness,...e.lightness},chroma:C(i.chroma,e.chroma),semanticMappings:C(i.semanticMappings,e.semanticMappings)};return e.pageBackgroundImage!==void 0&&(s.pageBackgroundImage=e.pageBackgroundImage),e.pageBackgroundImageOpacity!==void 0&&(s.pageBackgroundImageOpacity=e.pageBackgroundImageOpacity),e.boxBackgroundImage!==void 0&&(s.boxBackgroundImage=e.boxBackgroundImage),e.boxBackgroundImageOpacity!==void 0&&(s.boxBackgroundImageOpacity=e.boxBackgroundImageOpacity),e.vellumOpacity!==void 0&&(s.vellumOpacity=e.vellumOpacity),e.vellumBackgroundImage!==void 0&&(s.vellumBackgroundImage=e.vellumBackgroundImage),e.vellumBackgroundImageOpacity!==void 0&&(s.vellumBackgroundImageOpacity=e.vellumBackgroundImageOpacity),s}function C(i,e){if(!e)return{...i};const s={...i};for(const c of Object.keys(e)){const u=e[c];u!==void 0&&(s[c]=u)}return s}function z(i){const e=[],s=[];let c;try{c=atob(i)}catch{return e.push("Failed to decode Base64 string."),{valid:!1,errors:e,warnings:s}}let u;try{u=JSON.parse(c)}catch{return e.push("Decoded string is not valid JSON."),{valid:!1,errors:e,warnings:s}}if(typeof u!="object"||u===null||Array.isArray(u))return e.push("Theme must be a JSON object."),{valid:!1,errors:e,warnings:s};const o=u;"seedColor"in o&&(typeof o.seedColor!="string"?e.push("seedColor must be a string."):$(o.seedColor)||e.push(`seedColor is not a valid CSS color: "${o.seedColor}". Accepted forms: ${v}.`));for(const t of["fontBody","fontHeadings","fontBrand","fontMonospace"])t in o&&typeof o[t]!="string"&&e.push(`${t} must be a string.`);const g=["normal","bold","lighter","bolder"],p=["inherit","initial","unset","revert","revert-layer"];for(const t of["fontWeightBody","fontWeightHeadings","fontWeightBrand","fontWeightMonospace"])if(t in o){const n=o[t];if(typeof n=="number")(n<1||n>1e3)&&e.push(`${t} numeric value must be 1\u20131000 (got ${n}).`);else if(typeof n=="string"){const a=g.includes(n)||p.includes(n);if(/^\d+$/.test(n)){const x=Number(n);(x<1||x>1e3)&&e.push(`${t} numeric value must be 1\u20131000 (got "${n}").`)}else a||s.push(`${t} = "${n}" is not a standard font-weight value.`)}else e.push(`${t} must be a string or number.`)}"stylesheets"in o&&(Array.isArray(o.stylesheets)?o.stylesheets.some(t=>typeof t!="string")&&e.push("Every entry in stylesheets must be a string."):e.push("stylesheets must be an array of strings."));const m=[["rootFontSize",1,100],["borderRadius",0,10],["viewportMin",100,1e4],["viewportMax",200,1e4]];for(const[t,n,a]of m)if(t in o)if(typeof o[t]!="number"||!isFinite(o[t]))e.push(`${t} must be a finite number.`);else{const r=o[t];n!==void 0&&r<n&&e.push(`${t} must be \u2265 ${n} (got ${r}).`),a!==void 0&&r>a&&s.push(`${t} = ${r} is unusually high (max ${a}).`)}for(const t of["typeRatio","spaceRatio"])if(t in o)if(typeof o[t]!="number"||!isFinite(o[t]))e.push(`${t} must be a finite number.`);else{const n=o[t];n<=1&&e.push(`${t} must be > 1 (got ${n}).`);const a=t==="typeRatio"?1.3:2;n>a&&s.push(`${t} = ${n} is very high (max ${a}); scales may be extreme.`)}if("viewportMin"in o&&"viewportMax"in o){const t=o.viewportMin,n=o.viewportMax;typeof t=="number"&&typeof n=="number"&&t>=n&&e.push(`viewportMin (${t}) must be less than viewportMax (${n}).`)}if("angles"in o)if(typeof o.angles!="object"||o.angles===null)e.push("angles must be an object.");else{const t=o.angles;for(const n of["analogous","complementary","splitComplementary","triadic"])if(n in t)if(typeof t[n]!="number"||!isFinite(t[n]))e.push(`angles.${n} must be a finite number.`);else{const a=t[n];(a<0||a>360)&&s.push(`angles.${n} = ${a} is outside 0\u2013360.`)}}if("semanticHues"in o)if(typeof o.semanticHues!="object"||o.semanticHues===null)e.push("semanticHues must be an object.");else{const t=o.semanticHues;for(const n of["danger","success","warning"])if(n in t)if(typeof t[n]!="number"||!isFinite(t[n]))e.push(`semanticHues.${n} must be a finite number.`);else{const a=t[n];(a<0||a>360)&&s.push(`semanticHues.${n} = ${a} is outside 0\u2013360.`)}}if("variantChroma"in o)if(typeof o.variantChroma!="object"||o.variantChroma===null)e.push("variantChroma must be an object.");else{const t=o.variantChroma;for(const n of R)if(n in t)if(typeof t[n]!="number"||!isFinite(t[n]))e.push(`variantChroma["${n}"] must be a finite number.`);else{const a=t[n];a<0&&e.push(`variantChroma["${n}"] must be \u2265 0 (got ${a}).`),a>f&&s.push(`variantChroma["${n}"] = ${a} exceeds ${f}.`)}}if("lightness"in o)if(typeof o.lightness!="object"||o.lightness===null)e.push("lightness must be an object.");else{const t=o.lightness;for(const n of d)if(n in t)if(typeof t[n]!="number"||!isFinite(t[n]))e.push(`lightness.${n} must be a finite number.`);else{const a=t[n];(a<0||a>1)&&e.push(`lightness.${n} must be 0\u20131 (got ${a}).`)}}if("chroma"in o)if(typeof o.chroma!="object"||o.chroma===null)e.push("chroma must be an object.");else{const t=o.chroma;for(const n of h)if(n in t){const a=t[n];if(typeof a!="object"||a===null){e.push(`chroma.${n} must be { min, max }.`);continue}const r=a;(typeof r.min!="number"||r.min<0)&&e.push(`chroma.${n}.min must be \u2265 0.`),(typeof r.max!="number"||r.max<0||r.max>f)&&e.push(`chroma.${n}.max must be 0\u2013${f}.`),typeof r.min=="number"&&typeof r.max=="number"&&r.min>r.max&&e.push(`chroma.${n}.min (${r.min}) must be \u2264 max (${r.max}).`)}}if("semanticMappings"in o)if(typeof o.semanticMappings!="object"||o.semanticMappings===null)e.push("semanticMappings must be an object.");else{const t=o.semanticMappings;for(const n of h)if(n in t){const a=t[n];if(typeof a!="object"||a===null){e.push(`semanticMappings.${n} must be { source, lightness }.`);continue}const r=a;(typeof r.source!="string"||!B.includes(r.source))&&e.push(`semanticMappings.${n}.source must be one of: ${B.join(", ")}.`),(typeof r.lightness!="string"||!d.includes(r.lightness))&&e.push(`semanticMappings.${n}.lightness must be one of: ${d.join(", ")}.`)}}for(const t of["pageBackgroundImage","boxBackgroundImage","vellumBackgroundImage"])t in o&&typeof o[t]!="string"&&e.push(`${t} must be a string.`);for(const t of["pageBackgroundImageOpacity","boxBackgroundImageOpacity","vellumOpacity","vellumBackgroundImageOpacity"])if(t in o)if(typeof o[t]!="number"||!isFinite(o[t]))e.push(`${t} must be a finite number.`);else{const n=o[t];(n<0||n>1)&&e.push(`${t} must be 0\u20131 (got ${n}).`)}return{valid:e.length===0,errors:e,warnings:s}}const W=["angles","chroma","lightness","semanticHues","semanticMappings","variantChroma"];function F(i,e){const s={};for(const[c,u]of Object.entries(i))l(c)&&(s[c]=u);for(const[c,u]of Object.entries(e))u!==void 0&&l(c)&&(s[c]=u);for(const c of W){const u=i[c],o=e[c];if(u&&typeof u=="object"&&!Array.isArray(u)&&o&&typeof o=="object"&&!Array.isArray(o)){const g={};for(const[p,m]of Object.entries(u))l(p)&&(g[p]=m);for(const[p,m]of Object.entries(o))m!==void 0&&l(p)&&(g[p]=m);s[c]=g}}return s}function G(...i){let e={};for(const s of i){if(!s)continue;const c=_(s);c&&(e=F(e,c))}return k(e)}function O(i){return i.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")}const H={borderRadius:.1,fontBody:'"Noto Sans", sans-serif',fontWeightBody:"normal",fontHeadings:'"Inika", serif',fontWeightHeadings:"normal",fontBrand:'"Inika", serif',fontWeightBrand:"normal",fontMonospace:'"JetBrains Mono", monospace',fontWeightMonospace:"normal",seedColor:"oklch(.07 0.15 216)"};function J(i){return k({...H,pageBackgroundImage:`url("${O(i)}")`,pageBackgroundImageOpacity:.5})}function P(i){return k({...H,pageBackgroundImage:`url("${O(i)}")`,pageBackgroundImageOpacity:.55})}export{B as COLOR_SOURCES,A as DEFAULT_DARK_LIGHTNESS,D as DEFAULT_DARK_THEME,w as DEFAULT_LIGHT_LIGHTNESS,L as DEFAULT_LIGHT_THEME,y as DEFAULT_SEMANTIC_MAPPINGS,d as LIGHTNESS_KEYS,W as NESTED_THEME_KEYS,h as SEMANTIC_COLOR_NAMES,R as VARIANT_COLOR_SOURCES,P as buildTaprootDarkTheme,J as buildTaprootLightTheme,k as encodeTheme,G as layerThemes,F as mergePartials,K as mergeTheme,_ as parseTheme,U as semanticToCSS,z as validateTheme};
@@ -9,6 +9,7 @@ removing, or replacing non-code assets.
9
9
  | Asset area | Provenance | Notice |
10
10
  |------------|------------|--------|
11
11
  | `css/fonts/**` | Generated Google Fonts preview CSS with per-family notices generated from upstream Google Fonts metadata links. | `licenses/GOOGLE_FONTS_NOTICES.md` |
12
+ | `assets/shell-fonts.css`, `assets/shell-fonts/**` | Docs-only self-hosted Quicksand, Oswald, and Sometype Mono WOFF2 subsets under OFL 1.1, downloaded once from the Google Fonts css2 endpoint and checked in so the documentation shell never calls Google at runtime. | `licenses/THIRD_PARTY_NOTICES.md` |
12
13
  | `assets/icons.svg` | Tabler-derived SVG symbols plus Taproot-owned logo symbol. | `licenses/THIRD_PARTY_NOTICES.md` |
13
14
  | `*.ts` | Inline Tabler-style SVG templates and Taproot rendering helpers. | `licenses/THIRD_PARTY_NOTICES.md` |
14
15
  | `dist/shared/virtualizer/**` | Vendored Lit Virtualizer runtime source compiled into Espalier's package. | `licenses/THIRD_PARTY_NOTICES.md` |
@@ -25,8 +25,8 @@ until reviewed and adapted by qualified IP and technology-transactions counsel.
25
25
  - `ESPALIER_DEAL_READINESS_CHECKLIST.md` - practical readiness checklist for
26
26
  lawyer review, diligence, packaging, and outreach.
27
27
  - `THIRD_PARTY_NOTICES.md` - third-party open-source notices for materials
28
- copied into Espalier distribution artifacts, including Lit Virtualizer and
29
- Tabler Icons.
28
+ copied into Espalier distribution artifacts, including Lit Virtualizer,
29
+ Tabler Icons, and the self-hosted documentation shell fonts.
30
30
  - `asset-provenance.json` and `ASSET_PROVENANCE.md` - machine-readable and
31
31
  human-readable provenance records for generated fonts, icons, vendored
32
32
  runtime source, textures, demo datasets, and docs assets.
@@ -75,3 +75,124 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
75
75
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
76
76
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
77
77
  SOFTWARE.
78
+
79
+ ## Documentation Shell Fonts
80
+
81
+ The Espalier documentation site self-hosts the three faces its theme names, so
82
+ the site renders identically behind a strict Content-Security-Policy and no
83
+ visitor's browser calls Google to read the docs. The unmodified WOFF2 subsets
84
+ live in `assets/shell-fonts/` and are declared by `assets/shell-fonts.css`.
85
+ They are docs-only and are excluded from the published `@taprootio/espalier`
86
+ package.
87
+
88
+ - Project: Quicksand
89
+ - Upstream: https://github.com/andrew-paglinawan/QuicksandFamily
90
+ - Version: Google Fonts v37 (weight 400, `latin` and `latin-ext` subsets)
91
+ - License: SIL Open Font License 1.1
92
+ - Copyright: Copyright 2011 The Quicksand Project Authors (https://github.com/andrew-paglinawan/QuicksandFamily), with Reserved Font Name "Quicksand".
93
+
94
+ - Project: Oswald
95
+ - Upstream: https://github.com/googlefonts/OswaldFont
96
+ - Version: Google Fonts v57 (weight 700, `latin` and `latin-ext` subsets)
97
+ - License: SIL Open Font License 1.1
98
+ - Copyright: Copyright 2016 The Oswald Project Authors (https://github.com/googlefonts/OswaldFont)
99
+
100
+ - Project: Sometype Mono
101
+ - Upstream: https://github.com/googlefonts/sometype-mono
102
+ - Version: Google Fonts v4 (weight 400, `latin` and `latin-ext` subsets)
103
+ - License: SIL Open Font License 1.1
104
+ - Copyright: Copyright 2018 The Sometype Mono Project Authors (https://github.com/googlefonts/sometype-mono)
105
+
106
+ All three are covered by the same license text.
107
+
108
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
109
+ This license is copied below, and is also available with a FAQ at:
110
+ https://scripts.sil.org/OFL
111
+
112
+
113
+ -----------------------------------------------------------
114
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
115
+ -----------------------------------------------------------
116
+
117
+ PREAMBLE
118
+ The goals of the Open Font License (OFL) are to stimulate worldwide
119
+ development of collaborative font projects, to support the font creation
120
+ efforts of academic and linguistic communities, and to provide a free and
121
+ open framework in which fonts may be shared and improved in partnership
122
+ with others.
123
+
124
+ The OFL allows the licensed fonts to be used, studied, modified and
125
+ redistributed freely as long as they are not sold by themselves. The
126
+ fonts, including any derivative works, can be bundled, embedded,
127
+ redistributed and/or sold with any software provided that any reserved
128
+ names are not used by derivative works. The fonts and derivatives,
129
+ however, cannot be released under any other type of license. The
130
+ requirement for fonts to remain under this license does not apply
131
+ to any document created using the fonts or their derivatives.
132
+
133
+ DEFINITIONS
134
+ "Font Software" refers to the set of files released by the Copyright
135
+ Holder(s) under this license and clearly marked as such. This may
136
+ include source files, build scripts and documentation.
137
+
138
+ "Reserved Font Name" refers to any names specified as such after the
139
+ copyright statement(s).
140
+
141
+ "Original Version" refers to the collection of Font Software components as
142
+ distributed by the Copyright Holder(s).
143
+
144
+ "Modified Version" refers to any derivative made by adding to, deleting,
145
+ or substituting -- in part or in whole -- any of the components of the
146
+ Original Version, by changing formats or by porting the Font Software to a
147
+ new environment.
148
+
149
+ "Author" refers to any designer, engineer, programmer, technical
150
+ writer or other person who contributed to the Font Software.
151
+
152
+ PERMISSION & CONDITIONS
153
+ Permission is hereby granted, free of charge, to any person obtaining
154
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
155
+ redistribute, and sell modified and unmodified copies of the Font
156
+ Software, subject to the following conditions:
157
+
158
+ 1) Neither the Font Software nor any of its individual components,
159
+ in Original or Modified Versions, may be sold by itself.
160
+
161
+ 2) Original or Modified Versions of the Font Software may be bundled,
162
+ redistributed and/or sold with any software, provided that each copy
163
+ contains the above copyright notice and this license. These can be
164
+ included either as stand-alone text files, human-readable headers or
165
+ in the appropriate machine-readable metadata fields within text or
166
+ binary files as long as those fields can be easily viewed by the user.
167
+
168
+ 3) No Modified Version of the Font Software may use the Reserved Font
169
+ Name(s) unless explicit written permission is granted by the corresponding
170
+ Copyright Holder. This restriction only applies to the primary font name as
171
+ presented to the users.
172
+
173
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
174
+ Software shall not be used to promote, endorse or advertise any
175
+ Modified Version, except to acknowledge the contribution(s) of the
176
+ Copyright Holder(s) and the Author(s) or with their explicit written
177
+ permission.
178
+
179
+ 5) The Font Software, modified or unmodified, in part or in whole,
180
+ must be distributed entirely under this license, and must not be
181
+ distributed under any other license. The requirement for fonts to
182
+ remain under this license does not apply to any document created
183
+ using the Font Software.
184
+
185
+ TERMINATION
186
+ This license becomes null and void if any of the above conditions are
187
+ not met.
188
+
189
+ DISCLAIMER
190
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
191
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
192
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
193
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
194
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
195
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
196
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
197
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
198
+ OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "updatedAt": "2026-07-21",
3
+ "updatedAt": "2026-08-19",
4
4
  "entries": [
5
5
  {
6
6
  "id": "google-font-preview-assets",
@@ -316,6 +316,62 @@
316
316
  "noticeRequirement": "none",
317
317
  "noticePaths": [],
318
318
  "reviewStatus": "documented"
319
+ },
320
+ {
321
+ "id": "docs-shell-self-hosted-fonts",
322
+ "paths": [
323
+ "assets/shell-fonts.css",
324
+ "assets/shell-fonts/**"
325
+ ],
326
+ "assetType": "self-hosted web font faces (WOFF2 latin and latin-ext subsets) with a hand-authored @font-face stylesheet",
327
+ "classification": "third-party open font",
328
+ "owner": "The Quicksand, Oswald, and Sometype Mono project authors",
329
+ "source": "Google Fonts css2 endpoint, downloaded 2026-08-19 with a desktop Chrome User-Agent so the WOFF2 (not TrueType) sources were served: https://fonts.googleapis.com/css2?family=Quicksand&display=swap (Quicksand v37), https://fonts.googleapis.com/css2?family=Oswald:wght@700&display=swap (Oswald v57), https://fonts.googleapis.com/css2?family=Sometype+Mono&display=swap (Sometype Mono v4). These are the exact URLs <esp-root> built at runtime for the documentation theme before ESP0171. Upstream license metadata: https://github.com/google/fonts/blob/main/ofl/quicksand/METADATA.pb, https://github.com/google/fonts/blob/main/ofl/oswald/METADATA.pb, https://github.com/google/fonts/blob/main/ofl/sometypemono/METADATA.pb",
330
+ "license": "SIL Open Font License 1.1 (https://openfontlicense.org/open-font-license-official-text/) for all three families",
331
+ "transformations": [
332
+ "Downloaded the latin and latin-ext WOFF2 subsets named by each css2 response; the vietnamese and cyrillic subsets were not vendored",
333
+ "Renamed each subset to family-weight-subset.woff2; the font bytes themselves are unmodified",
334
+ "Reproduced the css2 @font-face declarations by hand in assets/shell-fonts.css, keeping the upstream unicode-range values and font-display: swap, with src pointing at the local /assets/shell-fonts/ paths"
335
+ ],
336
+ "subsetFiles": [
337
+ {
338
+ "path": "assets/shell-fonts/quicksand-400-latin.woff2",
339
+ "upstreamUrl": "https://fonts.gstatic.com/s/quicksand/v37/6xK-dSZaM9iE8KbpRA_LJ3z8mH9BOJvgkP8o58a-wg.woff2",
340
+ "sha256": "fee25690fddfdf52d7c88f8460260998c523b762e1a53fc91561b179835e3316"
341
+ },
342
+ {
343
+ "path": "assets/shell-fonts/quicksand-400-latin-ext.woff2",
344
+ "upstreamUrl": "https://fonts.gstatic.com/s/quicksand/v37/6xK-dSZaM9iE8KbpRA_LJ3z8mH9BOJvgkP8o58i-wi40.woff2",
345
+ "sha256": "db0347bb53f618c0d932a128ee262d847a6e7243110e31f869611d1b63450505"
346
+ },
347
+ {
348
+ "path": "assets/shell-fonts/oswald-700-latin.woff2",
349
+ "upstreamUrl": "https://fonts.gstatic.com/s/oswald/v57/TK3_WkUHHAIjg75cFRf3bXL8LICs1xZosUZiZQ.woff2",
350
+ "sha256": "aae665c75af89ea7cb7d8ccc8b0911ea72267442ebcd84f6e3efa041ad3b3c16"
351
+ },
352
+ {
353
+ "path": "assets/shell-fonts/oswald-700-latin-ext.woff2",
354
+ "upstreamUrl": "https://fonts.gstatic.com/s/oswald/v57/TK3_WkUHHAIjg75cFRf3bXL8LICs1xZosUhiZTaR.woff2",
355
+ "sha256": "ceb8c34b47b9e706ebbb402ca2db8055adea563a417590090bd5ac42df3c8272"
356
+ },
357
+ {
358
+ "path": "assets/shell-fonts/sometype-mono-400-latin.woff2",
359
+ "upstreamUrl": "https://fonts.gstatic.com/s/sometypemono/v4/70lGu745KGk_R3uxyq0WrROhAJiJsJ_eTWllpTAMGH9diwE.woff2",
360
+ "sha256": "9719b185dc14ff3e39ee87cc277f7679c4c9b95c10c1dbf7882adcf9c582f685"
361
+ },
362
+ {
363
+ "path": "assets/shell-fonts/sometype-mono-400-latin-ext.woff2",
364
+ "upstreamUrl": "https://fonts.gstatic.com/s/sometypemono/v4/70lGu745KGk_R3uxyq0WrROhAJiJsJ_eTWllpTAMGH9TiwFrsw.woff2",
365
+ "sha256": "a467270a93a4f13300e6ac87410414ef6b4bf762e150fca8f04d49d1a2b50abf"
366
+ }
367
+ ],
368
+ "packageInclusion": "not included in npm package",
369
+ "docsInclusion": "docs-site only — linked from docs/_includes/layout.vto and docs/404.vto and served from the Eleventy /assets passthrough, so the documentation shell renders its theme faces without runtime fonts.googleapis.com links",
370
+ "noticeRequirement": "required",
371
+ "noticePaths": [
372
+ "licenses/THIRD_PARTY_NOTICES.md"
373
+ ],
374
+ "reviewStatus": "documented"
319
375
  }
320
376
  ]
321
377
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taprootio/espalier",
3
- "version": "2.15.3",
3
+ "version": "2.16.0",
4
4
  "packageManager": "bun@1.3.12",
5
5
  "description": "Espalier — a themeable, accessible, framework-agnostic, enterprise-grade design system built on web standards and love.",
6
6
  "customElements": "custom-elements.json",