@excom/detect-features 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/.rush/temp/chunked-rush-logs/detect-features.apply-exports.chunks.jsonl +1 -0
  2. package/.rush/temp/chunked-rush-logs/detect-features.build_docs.chunks.jsonl +1 -0
  3. package/.rush/temp/chunked-rush-logs/detect-features.build_package-metas.chunks.jsonl +1 -0
  4. package/.rush/temp/operation/apply-exports/all.log +1 -0
  5. package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
  6. package/.rush/temp/operation/apply-exports/state.json +3 -0
  7. package/.rush/temp/operation/build_docs/all.log +1 -0
  8. package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
  9. package/.rush/temp/operation/build_docs/state.json +3 -0
  10. package/.rush/temp/operation/build_package-metas/all.log +1 -0
  11. package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
  12. package/.rush/temp/operation/build_package-metas/state.json +3 -0
  13. package/.rush/temp/shrinkwrap-deps.json +3 -0
  14. package/config/rig.json +5 -0
  15. package/detect-features.ts +127 -0
  16. package/index.ts +17 -0
  17. package/package.json +42 -0
  18. package/rush-logs/detect-features.apply-exports.cache.log +1 -0
  19. package/rush-logs/detect-features.apply-exports.log +1 -0
  20. package/rush-logs/detect-features.build_docs.cache.log +1 -0
  21. package/rush-logs/detect-features.build_docs.log +1 -0
  22. package/rush-logs/detect-features.build_package-metas.cache.log +1 -0
  23. package/rush-logs/detect-features.build_package-metas.log +1 -0
  24. package/support/custom-elements.json +244 -0
  25. package/support/demos/share.html +18 -0
  26. package/support/demos/simple.html +16 -0
  27. package/support/dist-docs/detect-features.md +135 -0
  28. package/support/docs/INTERNAL.md +9 -0
  29. package/support/docs/README.md +59 -0
  30. package/support/package-meta.json +94 -0
  31. package/support/tests/detect-features.test.ts +83 -0
  32. package/support/tests/share.view.test.ts +22 -0
  33. package/support/tests/simple.view.test.ts +22 -0
  34. package/tsconfig.json +5 -0
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
@@ -0,0 +1 @@
1
+ Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 36.20596599999999
3
+ }
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 50.03257800000006
3
+ }
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 100.76446399999986
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "../../packages/detect-features": "../../packages/detect-features:YAC8G8l1nkKknuTXH370JI0/DynCPkP81MNQcvbO6fA=:"
3
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
3
+ "rigPackageName": "@excom/heft-rig",
4
+ "rigProfile": "default"
5
+ }
@@ -0,0 +1,127 @@
1
+ import { ConstructorType, Neutron, TokenList } from "@excom/neutron";
2
+
3
+ // Feature name → sync support predicate. Prefer simple `in` / property
4
+ // checks; skip anything that needs probing, permissions, or user gestures.
5
+ const FEATURE_CHECKS = {
6
+ geolocation: () => "geolocation" in navigator,
7
+ vibrate: () => "vibrate" in navigator,
8
+ bluetooth: () => "bluetooth" in navigator,
9
+ usb: () => "usb" in navigator,
10
+ serial: () => "serial" in navigator,
11
+ hid: () => "hid" in navigator,
12
+ share: () => "share" in navigator,
13
+ clipboard: () => "clipboard" in navigator,
14
+ credentials: () => "credentials" in navigator,
15
+ "media-devices": () => "mediaDevices" in navigator,
16
+ "service-worker": () => "serviceWorker" in navigator,
17
+ storage: () => "storage" in navigator,
18
+ "wake-lock": () => "wakeLock" in navigator,
19
+ gpu: () => "gpu" in navigator,
20
+ locks: () => "locks" in navigator,
21
+ permissions: () => "permissions" in navigator,
22
+ connection: () => "connection" in navigator,
23
+ "user-agent-data": () => "userAgentData" in navigator,
24
+ battery: () => "getBattery" in navigator,
25
+ midi: () => "requestMIDIAccess" in navigator,
26
+ webxr: () => "xr" in navigator,
27
+ "virtual-keyboard": () => "virtualKeyboard" in navigator,
28
+ "window-controls-overlay": () => "windowControlsOverlay" in navigator,
29
+ contacts: () => "contacts" in navigator,
30
+ "local-storage": () => "localStorage" in globalThis,
31
+ "session-storage": () => "sessionStorage" in globalThis,
32
+ "indexed-db": () => "indexedDB" in globalThis,
33
+ notification: () => "Notification" in globalThis,
34
+ "payment-request": () => "PaymentRequest" in globalThis,
35
+ "web-socket": () => "WebSocket" in globalThis,
36
+ worker: () => "Worker" in globalThis,
37
+ "shared-worker": () => "SharedWorker" in globalThis,
38
+ "broadcast-channel": () => "BroadcastChannel" in globalThis,
39
+ "intersection-observer": () => "IntersectionObserver" in globalThis,
40
+ "resize-observer": () => "ResizeObserver" in globalThis,
41
+ "speech-synthesis": () => "speechSynthesis" in globalThis,
42
+ "eye-dropper": () => "EyeDropper" in globalThis,
43
+ "barcode-detector": () => "BarcodeDetector" in globalThis,
44
+ "file-system-access": () => "showOpenFilePicker" in globalThis,
45
+ webrtc: () => "RTCPeerConnection" in globalThis,
46
+ dialog: () => "HTMLDialogElement" in globalThis,
47
+ "view-transitions": () => "startViewTransition" in Document.prototype,
48
+ "cookie-store": () => "cookieStore" in globalThis,
49
+ "offscreen-canvas": () => "OffscreenCanvas" in globalThis,
50
+ } as const;
51
+
52
+ type FeatureName = keyof typeof FEATURE_CHECKS;
53
+
54
+ export type FeatureInfo = {
55
+ fullSupport: FeatureName[];
56
+ noSupport: FeatureName[];
57
+ };
58
+
59
+ /**
60
+ * Feature-detection provider — flags browser API support as CSS-selectable
61
+ * tokens on connect (geolocation, share, bluetooth, clipboard, and more).
62
+ * The same partitions are available as arrays on `.provision` for JS / Quark.
63
+ *
64
+ * @summary CSS-selectable feature support tokens.
65
+ *
66
+ * @example
67
+ * <detect-features></detect-features>
68
+ * <!-- detect-features:not([full-support~="geolocation"]) ... -->
69
+ */
70
+ export const DetectFeatures = Neutron({
71
+ tag: "detect-features",
72
+ props: {
73
+ /**
74
+ * @state
75
+ * Space-separated feature names fully supported by this browser.
76
+ * @values geolocation | vibrate | bluetooth | usb | serial | hid | share | clipboard | credentials | media-devices | service-worker | storage | wake-lock | gpu | locks | permissions | connection | user-agent-data | battery | midi | webxr | virtual-keyboard | window-controls-overlay | contacts | local-storage | session-storage | indexed-db | notification | payment-request | web-socket | worker | shared-worker | broadcast-channel | intersection-observer | resize-observer | speech-synthesis | eye-dropper | barcode-detector | file-system-access | webrtc | dialog | view-transitions | cookie-store | offscreen-canvas
77
+ */
78
+ fullSupport: TokenList,
79
+ /**
80
+ * @state
81
+ * Reserved for features with partial / conditional support. Not
82
+ * populated yet — see `INTERNAL.md`.
83
+ */
84
+ partialSupport: TokenList,
85
+ /**
86
+ * @state
87
+ * Space-separated feature names with no support in this browser.
88
+ * @values geolocation | vibrate | bluetooth | usb | serial | hid | share | clipboard | credentials | media-devices | service-worker | storage | wake-lock | gpu | locks | permissions | connection | user-agent-data | battery | midi | webxr | virtual-keyboard | window-controls-overlay | contacts | local-storage | session-storage | indexed-db | notification | payment-request | web-socket | worker | shared-worker | broadcast-channel | intersection-observer | resize-observer | speech-synthesis | eye-dropper | barcode-detector | file-system-access | webrtc | dialog | view-transitions | cookie-store | offscreen-canvas
89
+ */
90
+ noSupport: TokenList,
91
+ /**
92
+ * @state
93
+ * Reserved for features that require an explicit permission grant.
94
+ * Not populated yet — see `INTERNAL.md`.
95
+ */
96
+ grantedPermissions: TokenList,
97
+ /**
98
+ * @provision
99
+ * Same partitions as the token attributes, as string arrays for JS /
100
+ * Quark (`fullSupport`, `noSupport`). Not reflected as an attribute.
101
+ * @type FeatureInfo
102
+ */
103
+ provision: Object as unknown as ConstructorType<FeatureInfo>,
104
+ },
105
+ })
106
+ .defineMethods({
107
+ setFeatures: () => {
108
+ const entries = (Object.keys(FEATURE_CHECKS) as FeatureName[]).map(
109
+ (name) => [name, FEATURE_CHECKS[name]()] as const
110
+ );
111
+ const fullSupport = entries.filter(([, ok]) => ok).map(([name]) => name);
112
+ const noSupport = entries.filter(([, ok]) => !ok).map(([name]) => name);
113
+ return {
114
+ fullSupport,
115
+ noSupport,
116
+ provision: {
117
+ fullSupport,
118
+ noSupport,
119
+ },
120
+ };
121
+ },
122
+ })
123
+ .onConnected(
124
+ // no point in running this logic if the component has already done it
125
+ // in a previous connection
126
+ ({ wasMounted }) => !wasMounted && { setFeatures: [] }
127
+ );
package/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { DetectFeatures } from "./detect-features";
2
+
3
+ DetectFeatures.define();
4
+
5
+ export { DetectFeatures };
6
+
7
+ type T_HTMLDetectFeaturesElement = typeof DetectFeatures.CustomElement;
8
+ declare global {
9
+ interface HTMLDetectFeaturesElement extends T_HTMLDetectFeaturesElement {}
10
+ interface Window {
11
+ HTMLDetectFeaturesElement: HTMLDetectFeaturesElement;
12
+ }
13
+ interface HTMLElementTagNameMap {
14
+ "detect-features": HTMLDetectFeaturesElement;
15
+ }
16
+ }
17
+ export type { HTMLDetectFeaturesElement };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@excom/detect-features",
3
+ "version": "0.1.0",
4
+ "description": "<detect-features> custom element",
5
+ "license": "MIT",
6
+ "engines": {
7
+ "node": ">=24.13.0"
8
+ },
9
+ "type": "module",
10
+ "dependencies": {
11
+ "@excom/neutron": "^0.1.0"
12
+ },
13
+ "peerDependencies": {},
14
+ "devDependencies": {
15
+ "@excom/heft-rig": "^0.1.0"
16
+ },
17
+ "repository": {
18
+ "url": "excom-dev/nucleus",
19
+ "directory": "packages/detect-features"
20
+ },
21
+ "homepage": "https://github.com/excom-dev/nucleus/tree/main/packages/detect-features/support/docs/README.md",
22
+ "bugs": "https://github.com/excom-dev/nucleus/issues",
23
+ "keywords": [
24
+ "detect-features",
25
+ "neutron",
26
+ "custom-elements"
27
+ ],
28
+ "excom": {
29
+ "packageType": "kit-element"
30
+ },
31
+ "scripts": {
32
+ "build": "node node_modules/@excom/heft-rig/scripts/vite-build.mjs",
33
+ "build:watch": "node node_modules/@excom/heft-rig/scripts/vite-build-watch.mjs",
34
+ "format": "node node_modules/@excom/heft-rig/scripts/format.mjs",
35
+ "test": "node node_modules/@excom/heft-rig/scripts/vitest.mjs",
36
+ "coverage": "node node_modules/@excom/heft-rig/scripts/coverage.mjs",
37
+ "dev": "node node_modules/@excom/heft-rig/scripts/vite-dev.mjs",
38
+ "preview": "node node_modules/@excom/heft-rig/scripts/vite-preview.mjs",
39
+ "build:package-metas": "node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs",
40
+ "build:docs": "node node_modules/@excom/heft-rig/scripts/build-docs.mjs"
41
+ }
42
+ }
@@ -0,0 +1 @@
1
+ Caching has been disabled for this project's "apply-exports" command.
@@ -0,0 +1 @@
1
+ Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
@@ -0,0 +1 @@
1
+ This project does not define the caching behavior of the "build:docs" command, so caching has been disabled.
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
@@ -0,0 +1 @@
1
+ This project does not define the caching behavior of the "build:package-metas" command, so caching has been disabled.
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
@@ -0,0 +1,244 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "modules": [
4
+ {
5
+ "kind": "javascript-module",
6
+ "path": "detect-features.ts",
7
+ "declarations": [
8
+ {
9
+ "kind": "class",
10
+ "name": "DetectFeatures",
11
+ "customElement": true,
12
+ "tagName": "detect-features",
13
+ "summary": "CSS-selectable feature support tokens.",
14
+ "description": "Feature-detection provider — flags browser API support as CSS-selectable tokens on connect (geolocation, share, bluetooth, clipboard, and more). The same partitions are available as arrays on `.provision` for JS / Quark.",
15
+ "attributes": [
16
+ {
17
+ "name": "full-support",
18
+ "type": {
19
+ "text": "tokenlist"
20
+ },
21
+ "description": "Space-separated feature names fully supported by this browser.",
22
+ "fieldName": "fullSupport",
23
+ "values": [
24
+ "geolocation",
25
+ "vibrate",
26
+ "bluetooth",
27
+ "usb",
28
+ "serial",
29
+ "hid",
30
+ "share",
31
+ "clipboard",
32
+ "credentials",
33
+ "media-devices",
34
+ "service-worker",
35
+ "storage",
36
+ "wake-lock",
37
+ "gpu",
38
+ "locks",
39
+ "permissions",
40
+ "connection",
41
+ "user-agent-data",
42
+ "battery",
43
+ "midi",
44
+ "webxr",
45
+ "virtual-keyboard",
46
+ "window-controls-overlay",
47
+ "contacts",
48
+ "local-storage",
49
+ "session-storage",
50
+ "indexed-db",
51
+ "notification",
52
+ "payment-request",
53
+ "web-socket",
54
+ "worker",
55
+ "shared-worker",
56
+ "broadcast-channel",
57
+ "intersection-observer",
58
+ "resize-observer",
59
+ "speech-synthesis",
60
+ "eye-dropper",
61
+ "barcode-detector",
62
+ "file-system-access",
63
+ "webrtc",
64
+ "dialog",
65
+ "view-transitions",
66
+ "cookie-store",
67
+ "offscreen-canvas"
68
+ ]
69
+ },
70
+ {
71
+ "name": "partial-support",
72
+ "type": {
73
+ "text": "tokenlist"
74
+ },
75
+ "description": "Reserved for features with partial / conditional support. Not populated yet — see `INTERNAL.md`.",
76
+ "fieldName": "partialSupport"
77
+ },
78
+ {
79
+ "name": "no-support",
80
+ "type": {
81
+ "text": "tokenlist"
82
+ },
83
+ "description": "Space-separated feature names with no support in this browser.",
84
+ "fieldName": "noSupport",
85
+ "values": [
86
+ "geolocation",
87
+ "vibrate",
88
+ "bluetooth",
89
+ "usb",
90
+ "serial",
91
+ "hid",
92
+ "share",
93
+ "clipboard",
94
+ "credentials",
95
+ "media-devices",
96
+ "service-worker",
97
+ "storage",
98
+ "wake-lock",
99
+ "gpu",
100
+ "locks",
101
+ "permissions",
102
+ "connection",
103
+ "user-agent-data",
104
+ "battery",
105
+ "midi",
106
+ "webxr",
107
+ "virtual-keyboard",
108
+ "window-controls-overlay",
109
+ "contacts",
110
+ "local-storage",
111
+ "session-storage",
112
+ "indexed-db",
113
+ "notification",
114
+ "payment-request",
115
+ "web-socket",
116
+ "worker",
117
+ "shared-worker",
118
+ "broadcast-channel",
119
+ "intersection-observer",
120
+ "resize-observer",
121
+ "speech-synthesis",
122
+ "eye-dropper",
123
+ "barcode-detector",
124
+ "file-system-access",
125
+ "webrtc",
126
+ "dialog",
127
+ "view-transitions",
128
+ "cookie-store",
129
+ "offscreen-canvas"
130
+ ]
131
+ },
132
+ {
133
+ "name": "granted-permissions",
134
+ "type": {
135
+ "text": "tokenlist"
136
+ },
137
+ "description": "Reserved for features that require an explicit permission grant. Not populated yet — see `INTERNAL.md`.",
138
+ "fieldName": "grantedPermissions"
139
+ }
140
+ ],
141
+ "members": [
142
+ {
143
+ "kind": "field",
144
+ "name": "fullSupport",
145
+ "type": {
146
+ "text": "tokenlist"
147
+ },
148
+ "privacy": "public",
149
+ "readonly": true,
150
+ "description": "Space-separated feature names fully supported by this browser.",
151
+ "_neutron": {
152
+ "surface": "state"
153
+ }
154
+ },
155
+ {
156
+ "kind": "field",
157
+ "name": "partialSupport",
158
+ "type": {
159
+ "text": "tokenlist"
160
+ },
161
+ "privacy": "public",
162
+ "readonly": true,
163
+ "description": "Reserved for features with partial / conditional support. Not populated yet — see `INTERNAL.md`.",
164
+ "_neutron": {
165
+ "surface": "state"
166
+ }
167
+ },
168
+ {
169
+ "kind": "field",
170
+ "name": "noSupport",
171
+ "type": {
172
+ "text": "tokenlist"
173
+ },
174
+ "privacy": "public",
175
+ "readonly": true,
176
+ "description": "Space-separated feature names with no support in this browser.",
177
+ "_neutron": {
178
+ "surface": "state"
179
+ }
180
+ },
181
+ {
182
+ "kind": "field",
183
+ "name": "grantedPermissions",
184
+ "type": {
185
+ "text": "tokenlist"
186
+ },
187
+ "privacy": "public",
188
+ "readonly": true,
189
+ "description": "Reserved for features that require an explicit permission grant. Not populated yet — see `INTERNAL.md`.",
190
+ "_neutron": {
191
+ "surface": "state"
192
+ }
193
+ },
194
+ {
195
+ "kind": "field",
196
+ "name": "provision",
197
+ "type": {
198
+ "text": "FeatureInfo",
199
+ "expanded": "{ fullSupport: keyof typeof FEATURE_CHECKS[]; noSupport: keyof typeof FEATURE_CHECKS[]; }"
200
+ },
201
+ "privacy": "public",
202
+ "readonly": false,
203
+ "description": "Same partitions as the token attributes, as string arrays for JS / Quark (`fullSupport`, `noSupport`). Not reflected as an attribute.",
204
+ "_neutron": {
205
+ "surface": "option"
206
+ }
207
+ }
208
+ ],
209
+ "_neutron": {
210
+ "provisions": [
211
+ {
212
+ "name": "provision",
213
+ "type": {
214
+ "text": "FeatureInfo",
215
+ "expanded": "{ fullSupport: keyof typeof FEATURE_CHECKS[]; noSupport: keyof typeof FEATURE_CHECKS[]; }"
216
+ },
217
+ "description": "Same partitions as the token attributes, as string arrays for JS / Quark (`fullSupport`, `noSupport`). Not reflected as an attribute.",
218
+ "fieldName": "provision"
219
+ }
220
+ ]
221
+ }
222
+ }
223
+ ],
224
+ "exports": [
225
+ {
226
+ "kind": "js",
227
+ "name": "DetectFeatures",
228
+ "declaration": {
229
+ "name": "DetectFeatures",
230
+ "module": "detect-features.ts"
231
+ }
232
+ },
233
+ {
234
+ "kind": "custom-element-definition",
235
+ "name": "detect-features",
236
+ "declaration": {
237
+ "name": "DetectFeatures",
238
+ "module": "detect-features.ts"
239
+ }
240
+ }
241
+ ]
242
+ }
243
+ ]
244
+ }
@@ -0,0 +1,18 @@
1
+ <div>
2
+ <detect-features></detect-features>
3
+ <p>Web Share is available in this browser.</p>
4
+ <p role="status">
5
+ Web Share is not available — offer a copy-link fallback instead.
6
+ </p>
7
+ <small role="note">See "Live document" tab below to see the <code>full-support</code> and <code>no-support</code> attributes populated with all of your browser's features.</small>
8
+ <style>
9
+ #demo-detect-features-share {
10
+ &:has(detect-features[full-support~="share"]) p[role="status"] {
11
+ display: none;
12
+ }
13
+ &:has(detect-features:not([full-support~="share"])) p:not([role="status"]) {
14
+ display: none;
15
+ }
16
+ }
17
+ </style>
18
+ </div>
@@ -0,0 +1,16 @@
1
+ <div>
2
+ <detect-features></detect-features>
3
+ <p>Geolocation supported in this browser.</p>
4
+ <p role="alert">Geolocation is not supported in this browser.</p>
5
+ <small role="note">See "Live document" tab below to see the <code>full-support</code> and <code>no-support</code> attributes populated with all of your browser's features.</small>
6
+ <style>
7
+ #demo-detect-features-simple {
8
+ &:has(detect-features[full-support~="geolocation"]) p[role="alert"] {
9
+ display: none;
10
+ }
11
+ &:has(detect-features:not([full-support~="geolocation"])) p:not([role="alert"]) {
12
+ display: none;
13
+ }
14
+ }
15
+ </style>
16
+ </div>
@@ -0,0 +1,135 @@
1
+ # detect-features
2
+
3
+ Feature detection — gate content on real browser support with
4
+ plain CSS.
5
+
6
+
7
+ ```html
8
+ <div>
9
+ <detect-features></detect-features>
10
+ <p>Geolocation supported in this browser.</p>
11
+ <p role="alert">Geolocation is not supported in this browser.</p>
12
+ <small role="note">See "Live document" tab below to see the <code>full-support</code> and <code>no-support</code> attributes populated with all of your browser's features.</small>
13
+ <style>
14
+ #demo-detect-features-simple {
15
+ &:has(detect-features[full-support~="geolocation"]) p[role="alert"] {
16
+ display: none;
17
+ }
18
+ &:has(detect-features:not([full-support~="geolocation"])) p:not([role="alert"]) {
19
+ display: none;
20
+ }
21
+ }
22
+ </style>
23
+ </div>
24
+ ```
25
+
26
+
27
+ ## Features
28
+
29
+ - **Support tokens** Set once on connect for CSS / Quark targeting
30
+ - **CSS-selectable** Gate content with attribute selectors, no polyfills
31
+ - **JS / Quark data** `.provision` exposes `fullSupport` / `noSupport` arrays
32
+ - **Common Web APIs** Geolocation, share, bluetooth, clipboard,
33
+ media-devices, service-worker, payment-request, and many more
34
+
35
+ ## Installation
36
+
37
+
38
+ `@excom/detect-features` v0.1.0
39
+
40
+ ```bash
41
+ pnpm add @excom/detect-features
42
+ ```
43
+
44
+ ```bash
45
+ npm install @excom/detect-features
46
+ ```
47
+
48
+ ```bash
49
+ yarn add @excom/detect-features
50
+ ```
51
+
52
+ ### Import
53
+
54
+ ```ts
55
+ import "@excom/detect-features";
56
+ ```
57
+
58
+
59
+
60
+ ## Usage
61
+
62
+ Check `full-support` / `no-support` for a space-separated feature name.
63
+
64
+ ```html
65
+ <detect-features></detect-features>
66
+ ```
67
+
68
+ Use CSS or Quark to react to the feature detection:
69
+
70
+ ```css
71
+ detect-features:not([full-support~="geolocation"]) ~ .needs-location {
72
+ display: none;
73
+ }
74
+ ```
75
+
76
+ ### API Reference
77
+
78
+
79
+ #### Attributes
80
+
81
+ | Name | Surface | Type | Default | Values | Description |
82
+ | --- | --- | --- | --- | --- | --- |
83
+ | `full-support` | state | `tokenlist` | | `"geolocation"` \| `"vibrate"` \| `"bluetooth"` \| `"usb"` \| `"serial"` \| `"hid"` \| `"share"` \| `"clipboard"` \| `"credentials"` \| `"media-devices"` \| `"service-worker"` \| `"storage"` \| `"wake-lock"` \| `"gpu"` \| `"locks"` \| `"permissions"` \| `"connection"` \| `"user-agent-data"` \| `"battery"` \| `"midi"` \| `"webxr"` \| `"virtual-keyboard"` \| `"window-controls-overlay"` \| `"contacts"` \| `"local-storage"` \| `"session-storage"` \| `"indexed-db"` \| `"notification"` \| `"payment-request"` \| `"web-socket"` \| `"worker"` \| `"shared-worker"` \| `"broadcast-channel"` \| `"intersection-observer"` \| `"resize-observer"` \| `"speech-synthesis"` \| `"eye-dropper"` \| `"barcode-detector"` \| `"file-system-access"` \| `"webrtc"` \| `"dialog"` \| `"view-transitions"` \| `"cookie-store"` \| `"offscreen-canvas"` | Space-separated feature names fully supported by this browser. |
84
+ | `partial-support` | state | `tokenlist` | | | Reserved for features with partial / conditional support. Not populated yet — see `INTERNAL.md`. |
85
+ | `no-support` | state | `tokenlist` | | `"geolocation"` \| `"vibrate"` \| `"bluetooth"` \| `"usb"` \| `"serial"` \| `"hid"` \| `"share"` \| `"clipboard"` \| `"credentials"` \| `"media-devices"` \| `"service-worker"` \| `"storage"` \| `"wake-lock"` \| `"gpu"` \| `"locks"` \| `"permissions"` \| `"connection"` \| `"user-agent-data"` \| `"battery"` \| `"midi"` \| `"webxr"` \| `"virtual-keyboard"` \| `"window-controls-overlay"` \| `"contacts"` \| `"local-storage"` \| `"session-storage"` \| `"indexed-db"` \| `"notification"` \| `"payment-request"` \| `"web-socket"` \| `"worker"` \| `"shared-worker"` \| `"broadcast-channel"` \| `"intersection-observer"` \| `"resize-observer"` \| `"speech-synthesis"` \| `"eye-dropper"` \| `"barcode-detector"` \| `"file-system-access"` \| `"webrtc"` \| `"dialog"` \| `"view-transitions"` \| `"cookie-store"` \| `"offscreen-canvas"` | Space-separated feature names with no support in this browser. |
86
+ | `granted-permissions` | state | `tokenlist` | | | Reserved for features that require an explicit permission grant. Not populated yet — see `INTERNAL.md`. |
87
+
88
+ #### Provision
89
+
90
+ | Name | Type | Description |
91
+ | --- | --- | --- |
92
+ | `provision` | `FeatureInfo` (`{ fullSupport: keyof typeof FEATURE_CHECKS[]; noSupport: keyof typeof FEATURE_CHECKS[]; }`) | Same partitions as the token attributes, as string arrays for JS / Quark (`fullSupport`, `noSupport`). Not reflected as an attribute. |
93
+
94
+
95
+
96
+ ### Examples
97
+
98
+ #### Gate a whole section
99
+
100
+ `full-support` lists every feature this browser supports; `no-support`
101
+ lists the rest. Wrap a whole section instead of one element by pairing
102
+ `:has()` with the same attribute selector:
103
+
104
+ ```css
105
+ body:has(detect-features:not([full-support~="geolocation"])) #store-locator {
106
+ display: none;
107
+ }
108
+ ```
109
+
110
+ #### Web Share progressive UI
111
+
112
+ Show a share affordance only when `share` is listed in `full-support`.
113
+ Browsers without Web Share keep the fallback copy instead.
114
+
115
+
116
+ ```html
117
+ <div>
118
+ <detect-features></detect-features>
119
+ <p>Web Share is available in this browser.</p>
120
+ <p role="status">
121
+ Web Share is not available — offer a copy-link fallback instead.
122
+ </p>
123
+ <small role="note">See "Live document" tab below to see the <code>full-support</code> and <code>no-support</code> attributes populated with all of your browser's features.</small>
124
+ <style>
125
+ #demo-detect-features-share {
126
+ &:has(detect-features[full-support~="share"]) p[role="status"] {
127
+ display: none;
128
+ }
129
+ &:has(detect-features:not([full-support~="share"])) p:not([role="status"]) {
130
+ display: none;
131
+ }
132
+ }
133
+ </style>
134
+ </div>
135
+ ```
@@ -0,0 +1,9 @@
1
+ - `partial-support` and `granted-permissions` attributes exist on the
2
+ element but are never populated by `setFeatures` — only `full-support` /
3
+ `no-support` (and the matching `.provision` arrays) are computed today, via
4
+ simple presence checks in `FEATURE_CHECKS` (`detect-features.ts`). Don't
5
+ advertise partial support / granted permissions in public docs until
6
+ this is implemented.
7
+ - Keep new detections cheap and sync (`"x" in navigator` /
8
+ `"X" in globalThis`). Skip capability probes, permission queries, and
9
+ user-gesture-gated APIs.
@@ -0,0 +1,59 @@
1
+ # detect-features
2
+
3
+ Feature detection — gate content on real browser support with
4
+ plain CSS.
5
+
6
+ <include-content data-demo="simple"></include-content>
7
+
8
+ ## Features
9
+
10
+ - **Support tokens** Set once on connect for CSS / Quark targeting
11
+ - **CSS-selectable** Gate content with attribute selectors, no polyfills
12
+ - **JS / Quark data** `.provision` exposes `fullSupport` / `noSupport` arrays
13
+ - **Common Web APIs** Geolocation, share, bluetooth, clipboard,
14
+ media-devices, service-worker, payment-request, and many more
15
+
16
+ ## Installation
17
+
18
+ <include-content is-active template-ref="/views/install-section/install-section.html"></include-content>
19
+
20
+ ## Usage
21
+
22
+ Check `full-support` / `no-support` for a space-separated feature name.
23
+
24
+ ```html
25
+ <detect-features></detect-features>
26
+ ```
27
+
28
+ Use CSS or Quark to react to the feature detection:
29
+
30
+ ```css
31
+ detect-features:not([full-support~="geolocation"]) ~ .needs-location {
32
+ display: none;
33
+ }
34
+ ```
35
+
36
+ ### API Reference
37
+
38
+ <include-content is-active template-ref="/views/api-reference/api-reference.html"></include-content>
39
+
40
+ ### Examples
41
+
42
+ #### Gate a whole section
43
+
44
+ `full-support` lists every feature this browser supports; `no-support`
45
+ lists the rest. Wrap a whole section instead of one element by pairing
46
+ `:has()` with the same attribute selector:
47
+
48
+ ```css
49
+ body:has(detect-features:not([full-support~="geolocation"])) #store-locator {
50
+ display: none;
51
+ }
52
+ ```
53
+
54
+ #### Web Share progressive UI
55
+
56
+ Show a share affordance only when `share` is listed in `full-support`.
57
+ Browsers without Web Share keep the fallback copy instead.
58
+
59
+ <include-content data-demo="share"></include-content>
@@ -0,0 +1,94 @@
1
+ {
2
+ "shortName": "detect-features",
3
+ "package": {
4
+ "name": "@excom/detect-features",
5
+ "version": "0.1.0",
6
+ "description": "<detect-features> custom element",
7
+ "peerDependencies": {},
8
+ "excom": {
9
+ "packageType": "kit-element"
10
+ }
11
+ },
12
+ "demos": {
13
+ "share": "<div>\n <detect-features></detect-features>\n <p>Web Share is available in this browser.</p>\n <p role=\"status\">\n Web Share is not available — offer a copy-link fallback instead.\n </p>\n <small role=\"note\">See \"Live document\" tab below to see the <code>full-support</code> and <code>no-support</code> attributes populated with all of your browser's features.</small>\n <style>\n #demo-detect-features-share {\n &:has(detect-features[full-support~=\"share\"]) p[role=\"status\"] {\n display: none;\n }\n &:has(detect-features:not([full-support~=\"share\"])) p:not([role=\"status\"]) {\n display: none;\n }\n }\n </style>\n</div>\n",
14
+ "simple": "<div>\n <detect-features></detect-features>\n <p>Geolocation supported in this browser.</p>\n <p role=\"alert\">Geolocation is not supported in this browser.</p>\n <small role=\"note\">See \"Live document\" tab below to see the <code>full-support</code> and <code>no-support</code> attributes populated with all of your browser's features.</small>\n <style>\n #demo-detect-features-simple {\n &:has(detect-features[full-support~=\"geolocation\"]) p[role=\"alert\"] {\n display: none;\n }\n &:has(detect-features:not([full-support~=\"geolocation\"])) p:not([role=\"alert\"]) {\n display: none;\n }\n }\n </style>\n</div>"
15
+ },
16
+ "readme": "<h1 id=\"md-detect-features\">detect-features</h1>\n<p>Feature detection — gate content on real browser support with\nplain CSS.</p>\n<p><include-content data-demo=\"simple\"></include-content></p>\n<h2 id=\"md-features\">Features</h2>\n<ul>\n<li><strong>Support tokens</strong> Set once on connect for CSS / Quark targeting</li>\n<li><strong>CSS-selectable</strong> Gate content with attribute selectors, no polyfills</li>\n<li><strong>JS / Quark data</strong> <code>.provision</code> exposes <code>fullSupport</code> / <code>noSupport</code> arrays</li>\n<li><strong>Common Web APIs</strong> Geolocation, share, bluetooth, clipboard,\nmedia-devices, service-worker, payment-request, and many more</li>\n</ul>\n<h2 id=\"md-installation\">Installation</h2>\n<p><include-content is-active template-ref=\"/views/install-section/install-section.html\"></include-content></p>\n<h2 id=\"md-usage\">Usage</h2>\n<p>Check <code>full-support</code> / <code>no-support</code> for a space-separated feature name.</p>\n<include-content data-language=\"html\"><template>&lt;detect-features&gt;&lt;/detect-features&gt;</template></include-content>\n<p>Use CSS or Quark to react to the feature detection:</p>\n<include-content data-language=\"css\"><template>detect-features:not([full-support~=\"geolocation\"]) ~ .needs-location {\n display: none;\n}</template></include-content>\n<h3 id=\"md-api-reference\">API Reference</h3>\n<p><include-content is-active template-ref=\"/views/api-reference/api-reference.html\"></include-content></p>\n<h3 id=\"md-examples\">Examples</h3>\n<h4 id=\"md-gate-a-whole-section\">Gate a whole section</h4>\n<p><code>full-support</code> lists every feature this browser supports; <code>no-support</code>\nlists the rest. Wrap a whole section instead of one element by pairing\n<code>:has()</code> with the same attribute selector:</p>\n<include-content data-language=\"css\"><template>body:has(detect-features:not([full-support~=\"geolocation\"])) #store-locator {\n display: none;\n}</template></include-content>\n<h4 id=\"md-web-share-progressive-ui\">Web Share progressive UI</h4>\n<p>Show a share affordance only when <code>share</code> is listed in <code>full-support</code>.\nBrowsers without Web Share keep the fallback copy instead.</p>\n<p><include-content data-demo=\"share\"></include-content></p>\n",
17
+ "docs": {
18
+ "readme": "<h1 id=\"md-detect-features\">detect-features</h1>\n<p>Feature detection — gate content on real browser support with\nplain CSS.</p>\n<p><include-content data-demo=\"simple\"></include-content></p>\n<h2 id=\"md-features\">Features</h2>\n<ul>\n<li><strong>Support tokens</strong> Set once on connect for CSS / Quark targeting</li>\n<li><strong>CSS-selectable</strong> Gate content with attribute selectors, no polyfills</li>\n<li><strong>JS / Quark data</strong> <code>.provision</code> exposes <code>fullSupport</code> / <code>noSupport</code> arrays</li>\n<li><strong>Common Web APIs</strong> Geolocation, share, bluetooth, clipboard,\nmedia-devices, service-worker, payment-request, and many more</li>\n</ul>\n<h2 id=\"md-installation\">Installation</h2>\n<p><include-content is-active template-ref=\"/views/install-section/install-section.html\"></include-content></p>\n<h2 id=\"md-usage\">Usage</h2>\n<p>Check <code>full-support</code> / <code>no-support</code> for a space-separated feature name.</p>\n<include-content data-language=\"html\"><template>&lt;detect-features&gt;&lt;/detect-features&gt;</template></include-content>\n<p>Use CSS or Quark to react to the feature detection:</p>\n<include-content data-language=\"css\"><template>detect-features:not([full-support~=\"geolocation\"]) ~ .needs-location {\n display: none;\n}</template></include-content>\n<h3 id=\"md-api-reference\">API Reference</h3>\n<p><include-content is-active template-ref=\"/views/api-reference/api-reference.html\"></include-content></p>\n<h3 id=\"md-examples\">Examples</h3>\n<h4 id=\"md-gate-a-whole-section\">Gate a whole section</h4>\n<p><code>full-support</code> lists every feature this browser supports; <code>no-support</code>\nlists the rest. Wrap a whole section instead of one element by pairing\n<code>:has()</code> with the same attribute selector:</p>\n<include-content data-language=\"css\"><template>body:has(detect-features:not([full-support~=\"geolocation\"])) #store-locator {\n display: none;\n}</template></include-content>\n<h4 id=\"md-web-share-progressive-ui\">Web Share progressive UI</h4>\n<p>Show a share affordance only when <code>share</code> is listed in <code>full-support</code>.\nBrowsers without Web Share keep the fallback copy instead.</p>\n<p><include-content data-demo=\"share\"></include-content></p>\n"
19
+ },
20
+ "installation": {
21
+ "name": "@excom/detect-features",
22
+ "shortName": "detect-features",
23
+ "version": "0.1.0",
24
+ "description": "<detect-features> custom element",
25
+ "packageType": "kit-element",
26
+ "cdn": "<script src=\"https://unpkg.com/@excom/kit-utils/dist/index.umd.min.js\"></script>\n<script src=\"https://unpkg.com/@excom/neutron/dist/index.umd.min.js\"></script>\n<script src=\"https://unpkg.com/@excom/detect-features@0.1.0/dist/index.umd.min.js\"></script>",
27
+ "install": {
28
+ "npm": "npm install @excom/detect-features"
29
+ },
30
+ "imports": {
31
+ "js": "import \"@excom/detect-features\";",
32
+ "html": "<!-- import path to `node_modules` will depend on your build setup -->\n<script type=\"module\" src=\"/node_modules/@excom/detect-features\"></script>\n<link rel=\"stylesheet\" href=\"/node_modules/@excom/detect-features\">"
33
+ },
34
+ "peerDependencies": []
35
+ },
36
+ "elementApis": [
37
+ {
38
+ "tag": "detect-features",
39
+ "summary": "CSS-selectable feature support tokens.",
40
+ "kind": "class",
41
+ "attributes": [
42
+ {
43
+ "name": "full-support",
44
+ "type": "tokenlist",
45
+ "description": "Space-separated feature names fully supported by this browser.",
46
+ "fieldName": "fullSupport",
47
+ "surface": "state",
48
+ "values": "\"geolocation\" | \"vibrate\" | \"bluetooth\" | \"usb\" | \"serial\" | \"hid\" | \"share\" | \"clipboard\" | \"credentials\" | \"media-devices\" | \"service-worker\" | \"storage\" | \"wake-lock\" | \"gpu\" | \"locks\" | \"permissions\" | \"connection\" | \"user-agent-data\" | \"battery\" | \"midi\" | \"webxr\" | \"virtual-keyboard\" | \"window-controls-overlay\" | \"contacts\" | \"local-storage\" | \"session-storage\" | \"indexed-db\" | \"notification\" | \"payment-request\" | \"web-socket\" | \"worker\" | \"shared-worker\" | \"broadcast-channel\" | \"intersection-observer\" | \"resize-observer\" | \"speech-synthesis\" | \"eye-dropper\" | \"barcode-detector\" | \"file-system-access\" | \"webrtc\" | \"dialog\" | \"view-transitions\" | \"cookie-store\" | \"offscreen-canvas\""
49
+ },
50
+ {
51
+ "name": "granted-permissions",
52
+ "type": "tokenlist",
53
+ "description": "Reserved for features that require an explicit permission grant. Not populated yet — see <code>INTERNAL.md</code>.",
54
+ "fieldName": "grantedPermissions",
55
+ "surface": "state"
56
+ },
57
+ {
58
+ "name": "no-support",
59
+ "type": "tokenlist",
60
+ "description": "Space-separated feature names with no support in this browser.",
61
+ "fieldName": "noSupport",
62
+ "surface": "state",
63
+ "values": "\"geolocation\" | \"vibrate\" | \"bluetooth\" | \"usb\" | \"serial\" | \"hid\" | \"share\" | \"clipboard\" | \"credentials\" | \"media-devices\" | \"service-worker\" | \"storage\" | \"wake-lock\" | \"gpu\" | \"locks\" | \"permissions\" | \"connection\" | \"user-agent-data\" | \"battery\" | \"midi\" | \"webxr\" | \"virtual-keyboard\" | \"window-controls-overlay\" | \"contacts\" | \"local-storage\" | \"session-storage\" | \"indexed-db\" | \"notification\" | \"payment-request\" | \"web-socket\" | \"worker\" | \"shared-worker\" | \"broadcast-channel\" | \"intersection-observer\" | \"resize-observer\" | \"speech-synthesis\" | \"eye-dropper\" | \"barcode-detector\" | \"file-system-access\" | \"webrtc\" | \"dialog\" | \"view-transitions\" | \"cookie-store\" | \"offscreen-canvas\""
64
+ },
65
+ {
66
+ "name": "partial-support",
67
+ "type": "tokenlist",
68
+ "description": "Reserved for features with partial / conditional support. Not populated yet — see <code>INTERNAL.md</code>.",
69
+ "fieldName": "partialSupport",
70
+ "surface": "state"
71
+ }
72
+ ],
73
+ "events": [],
74
+ "slots": [],
75
+ "cssProperties": [],
76
+ "cssClasses": [],
77
+ "cssAliases": [],
78
+ "listens": [],
79
+ "commands": [],
80
+ "defaultActions": [],
81
+ "expectedChildren": [],
82
+ "provisions": [
83
+ {
84
+ "name": "provision",
85
+ "type": "FeatureInfo",
86
+ "typeExpanded": "{ fullSupport: keyof typeof FEATURE_CHECKS[]; noSupport: keyof typeof FEATURE_CHECKS[]; }",
87
+ "description": "Same partitions as the token attributes, as string arrays for JS / Quark (<code>fullSupport</code>, <code>noSupport</code>). Not reflected as an attribute.",
88
+ "fieldName": "provision"
89
+ }
90
+ ]
91
+ }
92
+ ],
93
+ "exportedFiles": {}
94
+ }
@@ -0,0 +1,83 @@
1
+ import "../../index";
2
+ import {
3
+ afterEach,
4
+ describe,
5
+ expect,
6
+ it,
7
+ } from "@excom/heft-rig/node_modules/vitest";
8
+ import { fixture } from "@excom/heft-rig/profiles/default/config/test-utils";
9
+
10
+ const FEATURES = [
11
+ "geolocation",
12
+ "vibrate",
13
+ "bluetooth",
14
+ "usb",
15
+ "serial",
16
+ "hid",
17
+ "share",
18
+ "clipboard",
19
+ "credentials",
20
+ "media-devices",
21
+ "service-worker",
22
+ "storage",
23
+ "wake-lock",
24
+ "gpu",
25
+ "locks",
26
+ "permissions",
27
+ "connection",
28
+ "user-agent-data",
29
+ "battery",
30
+ "midi",
31
+ "webxr",
32
+ "virtual-keyboard",
33
+ "window-controls-overlay",
34
+ "contacts",
35
+ "local-storage",
36
+ "session-storage",
37
+ "indexed-db",
38
+ "notification",
39
+ "payment-request",
40
+ "web-socket",
41
+ "worker",
42
+ "shared-worker",
43
+ "broadcast-channel",
44
+ "intersection-observer",
45
+ "resize-observer",
46
+ "speech-synthesis",
47
+ "eye-dropper",
48
+ "barcode-detector",
49
+ "file-system-access",
50
+ "webrtc",
51
+ "dialog",
52
+ "view-transitions",
53
+ "cookie-store",
54
+ "offscreen-canvas",
55
+ ] as const;
56
+
57
+ describe("detect-features", () => {
58
+ afterEach(() => {
59
+ document.body.innerHTML = "";
60
+ });
61
+
62
+ it("partitions each feature into full-support or no-support", async () => {
63
+ const el = fixture<HTMLDetectFeaturesElement>(
64
+ `<detect-features></detect-features>`,
65
+ );
66
+ const full = [...el.fullSupport];
67
+ const none = [...el.noSupport];
68
+ const all = [...full, ...none].sort();
69
+
70
+ expect(all).toEqual([...FEATURES].sort());
71
+ expect(new Set(all).size).toBe(all.length);
72
+ expect(full.includes("geolocation")).toBe("geolocation" in navigator);
73
+ expect(none.includes("geolocation")).toBe(!("geolocation" in navigator));
74
+ expect(full.includes("bluetooth")).toBe("bluetooth" in navigator);
75
+ expect(full.includes("share")).toBe("share" in navigator);
76
+ expect(full.includes("local-storage")).toBe("localStorage" in globalThis);
77
+ expect(full.includes("dialog")).toBe("HTMLDialogElement" in globalThis);
78
+ expect(el.provision).toEqual({
79
+ fullSupport: full,
80
+ noSupport: none,
81
+ });
82
+ });
83
+ });
@@ -0,0 +1,22 @@
1
+ import "../../index";
2
+ import {
3
+ afterEach,
4
+ describe,
5
+ expect,
6
+ it,
7
+ } from "@excom/heft-rig/profiles/default/config/test-utils";
8
+ import { mountView, readDemo } from "@excom/quark/support/tests/view-helpers";
9
+
10
+ describe("share view", () => {
11
+ afterEach(() => {
12
+ document.body.innerHTML = "";
13
+ });
14
+
15
+ it("classifies share support", async () => {
16
+ const { root } = await mountView(readDemo(import.meta.url, "share"));
17
+ const el = root.querySelector("detect-features")!;
18
+ expect(
19
+ el.hasAttribute("full-support") || el.hasAttribute("no-support"),
20
+ ).toBe(true);
21
+ });
22
+ });
@@ -0,0 +1,22 @@
1
+ import "../../index";
2
+ import {
3
+ afterEach,
4
+ describe,
5
+ expect,
6
+ it,
7
+ } from "@excom/heft-rig/profiles/default/config/test-utils";
8
+ import { mountView, readDemo } from "@excom/quark/support/tests/view-helpers";
9
+
10
+ describe("simple view", () => {
11
+ afterEach(() => {
12
+ document.body.innerHTML = "";
13
+ });
14
+
15
+ it("lists feature support on the element", async () => {
16
+ const { root } = await mountView(readDemo(import.meta.url, "simple"));
17
+ const el = root.querySelector("detect-features")!;
18
+ expect(
19
+ el.hasAttribute("full-support") || el.hasAttribute("no-support"),
20
+ ).toBe(true);
21
+ });
22
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@excom/heft-rig/profiles/default/config/tsconfig.json",
3
+ "include": ["./*.ts"],
4
+ "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
5
+ }