@rogieking/figui3 2.29.3 → 2.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/skills/nested-css-selectors/SKILL.md +96 -0
- package/components.css +6 -6
- package/fig.js +13 -6
- package/package.json +6 -3
- package/propkit.html +1158 -951
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nested-css-selectors
|
|
3
|
+
description: Implements CSS nesting and advanced selectors with progressive enhancement and maintainable specificity. Use when writing or refactoring nested CSS, or when the user mentions :has, :is, :where, :not, or complex selector composition.
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Nested CSS + Selectors
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Write modern, readable CSS using nesting and selector composition without breaking fallback behavior or creating specificity traps.
|
|
12
|
+
|
|
13
|
+
## Core Rules
|
|
14
|
+
|
|
15
|
+
1. Start with a stable baseline rule first.
|
|
16
|
+
2. Use nesting to improve readability, not to increase selector depth.
|
|
17
|
+
3. Keep selector chains short; avoid deeply coupled DOM paths.
|
|
18
|
+
4. Prefer low-specificity patterns (`:where(...)`) for reusable component styles.
|
|
19
|
+
5. Gate risky/brand-new behavior behind `@supports selector(...)` when needed.
|
|
20
|
+
|
|
21
|
+
## Nesting Guidelines
|
|
22
|
+
|
|
23
|
+
- Nest only when the child selector is semantically tied to the parent block.
|
|
24
|
+
- Keep nesting to ~2 levels in normal component styles.
|
|
25
|
+
- Use `&` explicitly for pseudo-classes, variants, and stateful modifiers.
|
|
26
|
+
- Avoid implicit descendant nesting that recreates fragile long selectors.
|
|
27
|
+
|
|
28
|
+
```css
|
|
29
|
+
.card {
|
|
30
|
+
padding: 12px;
|
|
31
|
+
|
|
32
|
+
&__title {
|
|
33
|
+
font-weight: 600;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&:hover {
|
|
37
|
+
background: var(--figma-color-bg-secondary);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Advanced Selector Guidelines
|
|
43
|
+
|
|
44
|
+
- Use `:is(...)` to group alternatives without repeating declarations.
|
|
45
|
+
- Use `:where(...)` when you want grouping with near-zero specificity cost.
|
|
46
|
+
- Use `:has(...)` for parent/state relationships that previously required JS classes.
|
|
47
|
+
- Keep `:not(...)` small and explicit; avoid broad negative matching.
|
|
48
|
+
|
|
49
|
+
```css
|
|
50
|
+
.field :is(input, select, textarea) {
|
|
51
|
+
font: inherit;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.panel :where(button, [role="button"]) {
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Progressive Enhancement Pattern
|
|
60
|
+
|
|
61
|
+
```css
|
|
62
|
+
/* baseline behavior */
|
|
63
|
+
.form-row {
|
|
64
|
+
border: 1px solid var(--figma-color-border);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* enhanced behavior only where supported */
|
|
68
|
+
@supports selector(.form-row:has(input:focus-visible)) {
|
|
69
|
+
.form-row:has(input:focus-visible) {
|
|
70
|
+
border-color: var(--figma-color-border-selected);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Specificity Guardrails
|
|
76
|
+
|
|
77
|
+
- Prefer class-based selectors over element+class combinations unless necessary.
|
|
78
|
+
- Avoid IDs in component styling.
|
|
79
|
+
- If a rule should be easy to override, wrap grouping parts in `:where(...)`.
|
|
80
|
+
- If a selector becomes hard to reason about, split it into two simpler rules.
|
|
81
|
+
|
|
82
|
+
## Refactor Workflow
|
|
83
|
+
|
|
84
|
+
1. Identify repeated selector/declaration blocks.
|
|
85
|
+
2. Consolidate with `:is(...)` or `:where(...)` where intent matches.
|
|
86
|
+
3. Introduce nesting inside the owning component block.
|
|
87
|
+
4. Recheck specificity effects (especially when replacing comma groups).
|
|
88
|
+
5. Verify hover/focus/disabled/error states are unchanged.
|
|
89
|
+
|
|
90
|
+
## Quick Checklist
|
|
91
|
+
|
|
92
|
+
- [ ] Baseline works without advanced selectors.
|
|
93
|
+
- [ ] Nesting depth remains shallow and readable.
|
|
94
|
+
- [ ] `:has(...)` usage is support-gated when compatibility matters.
|
|
95
|
+
- [ ] Specificity is intentional (`:where` for low-cost overrides).
|
|
96
|
+
- [ ] No fragile selector tied to incidental DOM structure.
|
package/components.css
CHANGED
|
@@ -792,8 +792,10 @@ fig-button {
|
|
|
792
792
|
color: var(--figma-color-text);
|
|
793
793
|
box-shadow: 0 0 0 1px var(--figma-color-bordertranslucent);
|
|
794
794
|
|
|
795
|
-
&:active
|
|
795
|
+
&:active,
|
|
796
|
+
&:has(:active) {
|
|
796
797
|
background-color: var(--figma-color-bg-secondary);
|
|
798
|
+
color: var(--figma-color-text);
|
|
797
799
|
}
|
|
798
800
|
}
|
|
799
801
|
|
|
@@ -1588,7 +1590,7 @@ fig-3d-rotate {
|
|
|
1588
1590
|
|
|
1589
1591
|
& > label {
|
|
1590
1592
|
align-self: stretch;
|
|
1591
|
-
color:
|
|
1593
|
+
color: var(--figma-color-text);
|
|
1592
1594
|
}
|
|
1593
1595
|
|
|
1594
1596
|
& > * {
|
|
@@ -2030,8 +2032,7 @@ fig-slider {
|
|
|
2030
2032
|
border: none;
|
|
2031
2033
|
position: relative;
|
|
2032
2034
|
z-index: 1;
|
|
2033
|
-
cursor:
|
|
2034
|
-
cursor: -webkit-grab;
|
|
2035
|
+
cursor: default;
|
|
2035
2036
|
box-shadow: var(--slider-handle-shadow);
|
|
2036
2037
|
}
|
|
2037
2038
|
|
|
@@ -2105,8 +2106,7 @@ fig-slider {
|
|
|
2105
2106
|
border: none;
|
|
2106
2107
|
position: relative;
|
|
2107
2108
|
z-index: 1;
|
|
2108
|
-
cursor:
|
|
2109
|
-
cursor: -moz-grab;
|
|
2109
|
+
cursor: default;
|
|
2110
2110
|
box-shadow: var(--slider-handle-shadow);
|
|
2111
2111
|
transition: var(--handle-transition);
|
|
2112
2112
|
}
|
package/fig.js
CHANGED
|
@@ -5237,8 +5237,11 @@ class FigImage extends HTMLElement {
|
|
|
5237
5237
|
}
|
|
5238
5238
|
connectedCallback() {
|
|
5239
5239
|
this.#src = this.getAttribute("src") || "";
|
|
5240
|
-
this.upload =
|
|
5241
|
-
|
|
5240
|
+
this.upload =
|
|
5241
|
+
this.hasAttribute("upload") && this.getAttribute("upload") !== "false";
|
|
5242
|
+
this.download =
|
|
5243
|
+
this.hasAttribute("download") &&
|
|
5244
|
+
this.getAttribute("download") !== "false";
|
|
5242
5245
|
this.label = this.getAttribute("label") || "Upload";
|
|
5243
5246
|
this.size = this.getAttribute("size") || "small";
|
|
5244
5247
|
this.innerHTML = this.#getInnerHTML();
|
|
@@ -5377,7 +5380,7 @@ class FigImage extends HTMLElement {
|
|
|
5377
5380
|
this.setAttribute("src", this.blob);
|
|
5378
5381
|
}
|
|
5379
5382
|
static get observedAttributes() {
|
|
5380
|
-
return ["src", "upload", "aspect-ratio", "fit"];
|
|
5383
|
+
return ["src", "upload", "download", "aspect-ratio", "fit"];
|
|
5381
5384
|
}
|
|
5382
5385
|
get src() {
|
|
5383
5386
|
return this.#src;
|
|
@@ -5400,9 +5403,13 @@ class FigImage extends HTMLElement {
|
|
|
5400
5403
|
this.#loadImage(this.#src);
|
|
5401
5404
|
}
|
|
5402
5405
|
}
|
|
5403
|
-
if (name === "upload"
|
|
5404
|
-
this.upload = newValue
|
|
5405
|
-
this.
|
|
5406
|
+
if (name === "upload") {
|
|
5407
|
+
this.upload = newValue !== null && newValue !== "false";
|
|
5408
|
+
this.innerHTML = this.#getInnerHTML();
|
|
5409
|
+
this.#updateRefs();
|
|
5410
|
+
}
|
|
5411
|
+
if (name === "download") {
|
|
5412
|
+
this.download = newValue !== null && newValue !== "false";
|
|
5406
5413
|
this.innerHTML = this.#getInnerHTML();
|
|
5407
5414
|
this.#updateRefs();
|
|
5408
5415
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rogieking/figui3",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.30.1",
|
|
4
4
|
"description": "A lightweight web components library for building Figma plugin and widget UIs with native look and feel",
|
|
5
5
|
"author": "Rogie King",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
34
|
"dev": "bun --hot server.ts",
|
|
35
|
-
"build": "bun build fig.js --minify --outdir dist"
|
|
35
|
+
"build": "bun build fig.js --minify --outdir dist",
|
|
36
|
+
"dev:propkit": "cd propkit && npm run dev",
|
|
37
|
+
"build:propkit": "cd propkit && npm run build"
|
|
36
38
|
},
|
|
37
39
|
"repository": {
|
|
38
40
|
"type": "git",
|
|
@@ -71,7 +73,8 @@
|
|
|
71
73
|
"a11y"
|
|
72
74
|
],
|
|
73
75
|
"devDependencies": {
|
|
74
|
-
"@types/bun": "latest"
|
|
76
|
+
"@types/bun": "latest",
|
|
77
|
+
"playwright": "^1.58.2"
|
|
75
78
|
},
|
|
76
79
|
"peerDependencies": {
|
|
77
80
|
"typescript": "^5.0.0"
|