Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/field-group-targeted-selectors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@styleframe/theme": patch
---

Fix field-group horizontal/vertical border joining to target known control classes (`.input`, `.textarea`, `.select`, `.button`, `.dropdown`) instead of all children (`*`), using `:where`/`:is` for correct specificity.
11 changes: 7 additions & 4 deletions theme/src/recipes/field-group/useFieldGroupRecipe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ describe("useFieldGroupRecipe", () => {
});

describe("seam selectors", () => {
const C = `:where(.input, .textarea, .select, .button, .dropdown)`;
const K = `:is(.input, .textarea, .select, .button, .dropdown)`;

it("flattens horizontal seams and grows fields", () => {
const s = createInstance();
useFieldGroupRecipe(s);
Expand All @@ -152,10 +155,10 @@ describe("useFieldGroupRecipe", () => {
expect(horizontal).toBeDefined();

expect(
findChildRule(horizontal, "& > *:not(:last-child)")?.declarations,
findChildRule(horizontal, `& > ${C}:has(~ ${K})`)?.declarations,
).toMatchObject({ borderRightWidth: "0" });
expect(
findChildRule(horizontal, "& > *:not(:first-child)")?.declarations,
findChildRule(horizontal, `& > ${C} ~ ${K}`)?.declarations,
).toMatchObject({ borderTopLeftRadius: "0" });

for (const query of ["& > .input", "& > .select", "& > .textarea"]) {
Expand All @@ -174,10 +177,10 @@ describe("useFieldGroupRecipe", () => {
expect(vertical).toBeDefined();

expect(
findChildRule(vertical, "& > *:not(:last-child)")?.declarations,
findChildRule(vertical, `& > ${C}:has(~ ${K})`)?.declarations,
).toMatchObject({ borderBottomWidth: "0" });
expect(
findChildRule(vertical, "& > *:not(:first-child)")?.declarations,
findChildRule(vertical, `& > ${C} ~ ${K}`)?.declarations,
).toMatchObject({ borderTopLeftRadius: "0" });
});

Expand Down
20 changes: 16 additions & 4 deletions theme/src/recipes/field-group/useFieldGroupRecipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,26 @@ export const useFieldGroupRecipe = createUseRecipe(
(s) => {
const { selector } = s;

const fieldGroupChildren = [
".input",
".textarea",
".select",
".button",
".dropdown",
];
const C = `:where(${fieldGroupChildren.join(", ")})`; // subject anchor, 0 specificity
const K = `:is(${fieldGroupChildren.join(", ")})`; // adjacency key, (0,1,0) specificity

// Horizontal: join controls side-by-side and let fields take the slack.
selector(".field-group.-horizontal", {
"& > *:not(:last-child)": {
// non-last control (has a following control) → merge right edge into the next
[`& > ${C}:has(~ ${K})`]: {
borderTopRightRadius: "0",
borderBottomRightRadius: "0",
borderRightWidth: "0",
},
"& > *:not(:first-child)": {
// non-first control (preceded by a control) → square the left edge
[`& > ${C} ~ ${K}`]: {
borderTopLeftRadius: "0",
borderBottomLeftRadius: "0",
},
Expand All @@ -80,12 +92,12 @@ export const useFieldGroupRecipe = createUseRecipe(

// Vertical: join controls top-to-bottom.
selector(".field-group.-vertical", {
"& > *:not(:last-child)": {
[`& > ${C}:has(~ ${K})`]: {
borderBottomLeftRadius: "0",
borderBottomRightRadius: "0",
borderBottomWidth: "0",
},
"& > *:not(:first-child)": {
[`& > ${C} ~ ${K}`]: {
borderTopLeftRadius: "0",
borderTopRightRadius: "0",
},
Expand Down
Loading