Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 8 additions & 0 deletions .changeset/lemon-shoes-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@stackoverflow/stacks": minor
"@stackoverflow/stacks-svelte": minor
---

Updated checkbox and radio styling in Stacks Classic and documentation for SHINE.
Added s-checkmark and s-check-control__checkmark to Stacks Classic
Added Radio, Checkbox, and Checkmark components to Stacks Svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ const checkboxTemplate = ({ component, testid, id }: any) =>
<label class="s-label" for="${id}">Label</label>
</div>`;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const checkmarkTemplate = ({ component, testid, id }: any) =>
html` <div class="s-check-control s-check-control__checkmark" data-testid="${testid}">
<label class="s-label" for="${id}">Label</label>
${component}
</div>`;

["checkbox", "radio"].forEach((type) => {
describe(type, () => {
// TODO include indeterminate
Expand All @@ -35,3 +42,27 @@ const checkboxTemplate = ({ component, testid, id }: any) =>
});
});
});

describe("checkmark", () => {
["checked", "unchecked"].forEach((state) => {
runA11yTests({
tag: "input",
baseClass: "s-checkmark",
attributes: {
name: "test-name",
id: "test-id",
type: "radio",
...(state === "checked" ? { checked: "checked" } : {}),
},
template: ({ component, testid }) =>
checkmarkTemplate({
component,
testid,
id: "test-id",
}),
options: {
testidSuffix: state,
},
});
});
});
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
.s-checkbox,
.s-radio {
--_ch-baw: var(--su-static1);
--_ch-bc: var(--bc-dark);
--_ch-bg: var(--white);
.s-radio,
.s-checkmark {
--_ch-bg-image: unset;

// CONTEXTUAL STYLES
fieldset[disabled] &,
&[disabled] {
cursor: not-allowed;
opacity: var(--_o-disabled-static);
}

.s-check-control & {
&[disabled] + .s-label {
&:extend(.is-disabled .s-label);
}
}

.s-check-group & {
Expand All @@ -26,14 +17,6 @@
flex-shrink: 0;
}

// INTERACTION
&:focus {
.focus-styles();
}

background-color: var(--_ch-bg);
border: var(--_ch-baw) solid var(--_ch-bc);

appearance: none;
cursor: pointer;
font-size: inherit;
Expand All @@ -44,6 +27,41 @@
width: 1em;
}

.s-checkbox,
.s-radio {
--_ch-baw: var(--su-static1);
--_ch-bc: var(--black-350);
--_ch-bg: var(--white);

// CONTEXTUAL STYLES
fieldset[disabled] &,
&[disabled] {
--_ch-bc: var(--theme-secondary-300);
}

.s-check-control & {
&[disabled] + .s-label {
color: var(--theme-secondary-300);
}
&:checked + .s-label,
&:indeterminate + .s-label {
color: var(--black-600);
}
&[disabled]:checked + .s-label,
&[disabled]:indeterminate + .s-label {
color: var(--theme-secondary-300);
}
}

// INTERACTION
&:focus {
.focus-styles();
}

background-color: var(--_ch-bg);
border: var(--_ch-baw) solid var(--_ch-bc);
}

.s-checkbox {
// Less variables for check svg fill color
@ch-bg-image-fill: .set-white()[default];
Expand Down Expand Up @@ -89,12 +107,21 @@
--_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='11' height='11' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2 4.5 h7 v2 h-7 z' fill='@{ch-bg-image-fill-esc}'/%3E%3C/svg%3E");
}

// Disabled state overrides checked state
fieldset[disabled] &,
&[disabled] {
&:checked, &:indeterminate {
--_ch-bc: var(--theme-secondary-300) !important;
--_ch-bg: var(--theme-secondary-300);
}
}

background-image: var(--_ch-bg-image);

background-position: center center;
background-repeat: no-repeat;
background-size: contain;
border-radius: var(--br-md);
border-radius: var(--su-static1);
}

.s-radio {
Expand All @@ -114,9 +141,107 @@
--_ch-bg: var(--white);
}

// Disabled state overrides checked state
fieldset[disabled] &,
&[disabled] {
&:checked {
--_ch-bc: var(--theme-secondary-300);
}
}

border-radius: var(--br-circle);
}

.s-checkmark {
// Less variables for check icon fill color
@ch-icon-fill: .set-black()[600];
@ch-icon-fill-dark: .set-black-dark()[400];
@ch-icon-fill-disabled: .set-black()[300];
@ch-icon-fill-disabled-dark: .set-black-dark()[300];
@ch-icon-fill-esc: escape("@{ch-icon-fill}"); // color escaped for URL usage
@ch-icon-fill-dark-esc: escape("@{ch-icon-fill-dark}"); // color escaped for URL usage
@ch-icon-fill-disabled-esc: escape("@{ch-icon-fill-disabled}"); // color escaped for URL usage
@ch-icon-fill-disabled-dark-esc: escape("@{ch-icon-fill-disabled-dark}"); // color escaped for URL usage

// Base styles
--_ch-baw: 0;
--_ch-bc: transparent;
--_ch-bg: transparent;

// CONTEXTUAL STYLES
.s-check-control & {
&[disabled] {
~ .s-label {
color: var(--theme-secondary-300) !important;
}
}
&:checked {
~ .s-label {
color: var(--black-600) !important;
}
}
&[disabled]:checked {
~ .s-label {
color: var(--theme-secondary-300) !important;
}
}
}

// Handle label that comes before input using :has() on parent
.s-check-control:has(&:checked) .s-label {
color: var(--black-600) !important;
}

.s-check-control:has(&[disabled]) .s-label {
color: var(--theme-secondary-300) !important;
}

.s-check-control:has(&[disabled]:checked) .s-label {
color: var(--theme-secondary-300) !important;
}

// No focus style
&:focus {
outline: 0;
box-shadow: none;
}

// Remove all button-like styling
background-color: transparent;
border: 0;

// CONTEXTUAL STYLES
.dark-mode({
&:checked {
--_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='16' height='16' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.1567 5.00146L17.6265 5.53174L7.54639 15.6128L7.01611 16.1431L1.98584 11.1128L3.04639 10.0522L7.01514 14.021L16.5659 4.47119L17.0962 3.94092L18.1567 5.00146Z' fill='@{ch-icon-fill-dark-esc}'/%3E%3C/svg%3E");
}
fieldset[disabled] &,
&[disabled] {
&:checked {
--_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='16' height='16' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.1567 5.00146L17.6265 5.53174L7.54639 15.6128L7.01611 16.1431L1.98584 11.1128L3.04639 10.0522L7.01514 14.021L16.5659 4.47119L17.0962 3.94092L18.1567 5.00146Z' fill='@{ch-icon-fill-disabled-dark-esc}'/%3E%3C/svg%3E");
}
}
});

// STATES
&:checked {
--_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='16' height='16' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.1567 5.00146L17.6265 5.53174L7.54639 15.6128L7.01611 16.1431L1.98584 11.1128L3.04639 10.0522L7.01514 14.021L16.5659 4.47119L17.0962 3.94092L18.1567 5.00146Z' fill='@{ch-icon-fill-esc}'/%3E%3C/svg%3E");
}

// Disabled state overrides checked state
fieldset[disabled] &,
&[disabled] {
&:checked {
--_ch-bg-image: url("data:image/svg+xml;,%3Csvg width='16' height='16' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.1567 5.00146L17.6265 5.53174L7.54639 15.6128L7.01611 16.1431L1.98584 11.1128L3.04639 10.0522L7.01514 14.021L16.5659 4.47119L17.0962 3.94092L18.1567 5.00146Z' fill='@{ch-icon-fill-disabled-esc}'/%3E%3C/svg%3E");
}
}

background-image: var(--_ch-bg-image);
background-position: center center;
background-repeat: no-repeat;
background-size: contain;
}

.s-checkbox,
.s-radio:not(:checked) {
.validation-states(ch);
Expand All @@ -132,9 +257,27 @@

// CHILD ELEMENTS
.s-label {
color: var(--black-500);
font-size: var(--fs-body1);
font-weight: normal;
}


&&__checkmark {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this instead of checking against child elements because I thought it would be cleaner, let me know if I should change this

--_cc-ai: center; // Override to center align for checkmark
gap: var(--su-static4);

.s-checkmark {
align-self: baseline;
}

// Style icon when checkmark is disabled
&:has(.s-checkmark[disabled]) svg,
&:has(.s-checkmark[disabled]) .svg-icon {
color: var(--theme-secondary-300);
}
}

align-items: var(--_cc-ai);
display: flex;
gap: var(--su8);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const checkboxTemplate = ({ component, testid }: any) =>
${component}
</div>`;

["checkbox", "radio"].forEach((type) => {
["checkbox", "radio", "checkmark"].forEach((type) => {
describe(type, () => {
// TODO include indeterminate
["checked", "unchecked"].forEach((state) => {
Expand Down
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Loading
Loading