Skip to content

Implement checkbox #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 9, 2025
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
### 0.0.7 - 19.12.2024
# 0.0.8 - 19.12.2024
- Lagt til checkbox-komponent
- Lagt til alert-komponent

# 0.0.7 - 19.12.2024
- Importert alle ikoner fra figma på nytt for å fikse padding i ikon-problem
- Lagt til 3 nye ikoner (check-circle-filled, person-card og chat-add)
- Lagt til støtte for accent-color i ikoner
24 changes: 24 additions & 0 deletions packages/lib/components/checkbox/Overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Canvas, Meta, Source, Subtitle, Title } from "@storybook/blocks";

import * as stories from "./checkbox.stories";

<Meta of={stories} />

<Title />
<Subtitle>The checkbox component is implemented using only CSS.</Subtitle>

### Default
<Canvas of={stories.Default} />

### Checkbox list
If you need a vertical list of checkboxes, you can use the CSS class `.cx-checkbox-list`.
<Canvas of={stories.CheckboxList} />

### Indeterminate
A checkbox is displayed as indeterminate if it is put in an indeterminate state. It can also be triggered by applying the CSS class `.cx-checkbox--indeterminate`.
The indeterminate state is not something that is automatically applied to an native checkbox element, and requires use of JavaScript to be toggled.
<Canvas of={stories.Indeterminate} />

### Error
A checkbox receives error styling when the input is considered invalid. It can also be triggered by applying the CSS class `.cx-checkbox--invalid`.
<Canvas of={stories.ErrorState} />
136 changes: 136 additions & 0 deletions packages/lib/components/checkbox/checkbox.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
label.cx-checkbox {
display: inline-flex;
gap: var(--cx-spacing-2);
align-items: center;
cursor: pointer;

font-size: 1rem;
font-weight: 400;
color: var(--cx-color-text-primary);
line-height: 1rem;
}

label.cx-checkbox:is(:hover, :has(:focus-visible)) .cx-checkbox__checkmark {
--cx-gradient-highlight: var(--cx-color-blue);
--cx-gradient-background: var(--cx-color-grey-700);
--border-color: transparent;
}

label.cx-checkbox:has(:focus-visible) .cx-checkbox__checkmark {
outline: 2px auto var(--cx-color-border-accent-1);
outline-offset: 2px;
}

label.cx-checkbox:active .cx-checkbox__checkmark {
scale: 0.97;
}

label.cx-checkbox:has(input:checked) .cx-checkbox__checkmark {
--cx-gradient-highlight: var(--cx-color-background-accent-5);
--cx-gradient-background: var(--cx-color-background-accent-5);
--border-color: transparent;

&::before,
&::after {
scale: 1;
}

&::after {
transition-delay: 150ms;
}
}

label.cx-checkbox:has(input:user-invalid),
label.cx-checkbox.cx-checkbox--invalid {
.cx-checkbox__checkmark {
--border-color: var(--cx-color-signal-danger);
--border-thickness: 2px;
}

&:hover .cx-checkbox__checkmark {
--border-color: transparent;
}
}

label.cx-checkbox:has(input:indeterminate),
label.cx-checkbox.cx-checkbox--indeterminate {
.cx-checkbox__checkmark {
--cx-gradient-highlight: var(--cx-color-background-accent-5);
--cx-gradient-background: var(--cx-color-background-accent-5);
--border-color: transparent;
scale: 1;

&::before,
&::after {
scale: 1;
top: 11px;
rotate: 0deg;
}

&::before {
left: 6px;
}

&::after {
left: 7px;
}
}
}

.cx-checkbox__checkmark {
--border-color: var(--cx-color-border-primary);
--border-thickness: 1px;

position: relative;
width: var(--cx-spacing-6);
height: var(--cx-spacing-6);
border-radius: var(--cx-spacing-1);
box-shadow: inset 0 0 0 var(--border-thickness) var(--border-color);
background-image: linear-gradient(62deg, var(--cx-gradient-highlight) 5%, var(--cx-gradient-background) 91%);
transition: --cx-gradient-highlight 300ms ease, --cx-gradient-background 300ms ease, box-shadow 300ms ease;

&::before,
&::after {
content: "";
position: absolute;
height: 2px;
border-radius: 99px;
background-color: var(--cx-color-text-static-dark);
transition-duration: 200ms;
transition-timing-function: var(--ease-4);
transition-property: scale, rotate, left, top;
transform-origin: left center;
scale: 0 1;
}

&::before {
width: 6px;
rotate: 45deg;
top: 11px;
left: 7px;
transition-delay: 100ms;
}

&::after {
width: 11px;
rotate: -45deg;
top: 15px;
left: 10px;
}
}

label.cx-checkbox input[type="checkbox"] {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}

.cx-checkbox-list {
display: flex;
flex-direction: column;
gap: var(--cx-spacing-2);
}
58 changes: 58 additions & 0 deletions packages/lib/components/checkbox/checkbox.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';

export default {
title: 'Components/Checkbox',
} satisfies Meta;

export const Default: StoryObj = {
render: () => html`
<label class="cx-checkbox">
<input type="checkbox" />
<div class="cx-checkbox__checkmark"></div>
Check me
</label>
`,
};

export const CheckboxList: StoryObj = {
render: () => html`
<fieldset class="cx-checkbox-list">
<label class="cx-checkbox">
<input type="checkbox" />
<div class="cx-checkbox__checkmark"></div>
Checkbox no 1
</label>
<label class="cx-checkbox">
<input type="checkbox" />
<div class="cx-checkbox__checkmark"></div>
Checkbox no 2
</label>
<label class="cx-checkbox">
<input type="checkbox" />
<div class="cx-checkbox__checkmark"></div>
Checkbox no 3
</label>
</fieldset>
`,
};

export const Indeterminate: StoryObj = {
render: () => html`
<label class="cx-checkbox cx-checkbox--indeterminate">
<input type="checkbox" />
<div class="cx-checkbox__checkmark"></div>
Check me
</label>
`,
};

export const ErrorState: StoryObj = {
render: () => html`
<label class="cx-checkbox cx-checkbox--invalid">
<input type="checkbox" />
<div class="cx-checkbox__checkmark"></div>
Check me
</label>
`,
};
6 changes: 3 additions & 3 deletions packages/lib/components/icon/Overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Then simply use the component and provide the name of the icon you wish to displ
If you try to use an icon that is not registered, the component throws an error.

<br />
## Icon overview
### Icon overview

<IconGallery>
{Object.entries(cxIcons)
Expand All @@ -75,14 +75,14 @@ If you try to use an icon that is not registered, the component throws an error.
))}
</IconGallery>

## Sizing
### Sizing

The icon component can be sized through the `size` prop. The prop accepts the same values as our spacing tokens, in other words a number between 1 and 20.
The numbers are on a relative scale, and aligns with our 4px grid. If you want your icon to be 32px, use `size="8"` (since 32 / 4 = 8).

<Canvas of={stories.Sizing} />

## Color
### Color

By default, the icon inherits the font color of the container it's within.
If you want to use a custom color, apply a CSS class where you use the `color` property to colorize the icon.
Expand Down
6 changes: 3 additions & 3 deletions packages/lib/components/tabs/Overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ import * as stories from "./tabs.stories";
It should always be one tab selected by default.
</Subtitle>

## Tab link
### Tab link
Tabs are most commonly used for top level navigation. The recommended way of doing this is by using routing,
since this leverages URL state. In most cases you'll therefore have links in your application, styled
as tabs. If you use Tanstack Router, this means using the `Link` component provided by Tanstack Router.
Simply put the `.cx-tab` class on there, and you're good to go. To set a tab as visually active, apply the
`.cx-tab--active` class.
<Canvas of={stories.TabLink} />

## Tab button
### Tab button
In some rare occasions, you'll want to route programmatically. This may be if you for instance need to do some
other tasks before navigating. In these scenarios, it's recommended to use a `button` element with a click event.
As with the `a` element, simply use the `.cx-tab` class.
<Canvas of={stories.TabButton} />

## Web component
### Web component
Sometimes, tabs are not used "top level", and are only used to shuffle between blocks of content on a part of the page.
In these scenarios we provide a web component/React component. This component handles all of the accessibility and
state for you, so you only need to provide the title for the tab, and the content that goes within.
Expand Down
2 changes: 2 additions & 0 deletions packages/lib/components/tabs/tab-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class TabGroup extends LitElement {
(tabHeader, index) => html`
<label
class=${classMap({ 'cx-tab': true, 'cx-outline-on-focus-within': true, 'cx-tab--active': this.activeTabIndex === index })}
role="none"
>
<input
class="cx-visually-hidden"
Expand All @@ -88,6 +89,7 @@ export class TabGroup extends LitElement {
role="tabpanel"
id=${`tabpanel-${this.activeTabIndex}`}
aria-labelledby=${`tab-${this.activeTabIndex}`}
tabindex="0"
>
<slot class="content-container" @slotchange=${this.setTabContentIndexes}></slot>
</div>
Expand Down
1 change: 1 addition & 0 deletions packages/lib/global-styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

/* Components */
@import "components/button/button.css";
@import "components/checkbox/checkbox.css";
@import "components/chips/chip.css";
@import "components/form-field/form-field.css";
@import "components/input/input.css";
Expand Down
Loading