-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add a `Tabs` component * feat: improve Tabs styling and add smaller size * fix: remove radius on mobile selected tab * test: add test for Tabs * docs: add Storybook docs for Tabs * chore: add Tabs export to index.ts file * chore: resolve some Tabs styling issues * test: add click test for tabs * chore: show not allowed cursor for disabled tabs * feat: redo the hover state for Tabs * chore: remove empty CSS ruleset
- Loading branch information
1 parent
5d6366c
commit f06e207
Showing
8 changed files
with
428 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
.tabs { | ||
--tab-font: var(--bloom-font-alt-sans); | ||
--tab-font-size: var(--bloom-type-body); | ||
--tab-font-size-sm: var(--bloom-type-label); | ||
--tab-font-weight: var(--bloom-font-weight-semibold); | ||
--tab-font-weight-sm: var(--bloom-font-weight-regular); | ||
--tab-background-color: var(--bloom-bg-color-surface); | ||
--tab-label-color: var(--bloom-text-color-light); | ||
--tab-padding-inline: var(--bloom-s6); | ||
--tab-padding-block: var(--bloom-s4); | ||
--tab-padding-inline-sm: var(--bloom-s6); | ||
--tab-padding-block-sm: var(--bloom-s3); | ||
--tab-border-radius: var(--bloom-rounded-lg); | ||
--tab-border-color: var(--bloom-border-color); | ||
--tab-border-width: var(--bloom-border-1); | ||
--tab-hover-background-color: var(--bloom-bg-color-surface-primary); | ||
--tab-active-label-color: var(--bloom-color-on-surface); | ||
--tab-active-indicator-width: var(--bloom-border-2); | ||
--tab-active-indicator-color: var(--bloom-color-primary); | ||
--tab-panel-padding: var(--bloom-spacer-section); | ||
--tab-panel-background-color: var(--bloom-bg-color-surface); | ||
} | ||
|
||
.tabs-tablist { | ||
display: flex; | ||
font-family: var(--tab-font); | ||
font-size: var(--tab-font-size); | ||
font-weight: var(--tab-font-weight); | ||
margin: 0; | ||
margin-block-end: -1px; | ||
padding: 0; | ||
list-style-type: none; | ||
|
||
&[data-size="sm"] { | ||
font-size: var(--tab-font-size-sm); | ||
font-weight: var(--tab-font-weight-sm); | ||
} | ||
} | ||
|
||
.tabs-tab { | ||
background-color: var(--tab-background-color); | ||
color: var(--tab-label-color); | ||
padding-inline: var(--tab-padding-inline); | ||
padding-block: var(--tab-padding-block) calc(var(--tab-padding-block) - var(--tab-active-indicator-width)); | ||
|
||
border: var(--tab-border-width) solid var(--tab-border-color); | ||
border-radius: 0; | ||
|
||
cursor: pointer; | ||
position: relative; | ||
|
||
[data-size="sm"] > & { | ||
padding-inline: var(--tab-padding-inline-sm); | ||
padding-block: var(--tab-padding-block-sm) calc(var(--tab-padding-block-sm) - var(--tab-active-indicator-width)); | ||
} | ||
|
||
&:focus-visible { | ||
outline: 2px solid var(--bloom-color-accent-cool); | ||
outline-offset: 2px; | ||
} | ||
|
||
&:hover { | ||
color: var(--tab-active-indicator-color); | ||
background-color: var(--tab-hover-background-color); | ||
} | ||
|
||
&:not(:last-of-type) { | ||
border-right-width: 0; | ||
} | ||
|
||
&:first-of-type { | ||
border-top-left-radius: var(--tab-border-radius); | ||
} | ||
|
||
&:last-of-type { | ||
border-top-right-radius: var(--tab-border-radius); | ||
} | ||
|
||
&[aria-selected="true"] { | ||
border-bottom-color: var(--tab-active-indicator-color); | ||
border-bottom-width: var(--tab-active-indicator-width); | ||
z-index: 1; | ||
} | ||
|
||
&:not(:hover)[aria-selected="true"] { | ||
color: var(--tab-active-label-color); | ||
} | ||
|
||
&[aria-disabled="true"] { | ||
color: var(--bloom-text-color-disabled); | ||
border-bottom-width: var(--tab-border-width); | ||
cursor: not-allowed; | ||
} | ||
} | ||
|
||
.tabs-panel { | ||
padding: var(--tab-panel-padding); | ||
background-color: var(--tab-panel-background-color); | ||
border: var(--tab-border-width) solid var(--tab-border-color); | ||
|
||
&:not(.is-active) { | ||
display: none; | ||
} | ||
} | ||
|
||
@media (max-width: 640px) { | ||
.tabs-tablist { | ||
flex-direction: column; | ||
} | ||
|
||
.tabs-tab { | ||
border-color: var(--tab-border-color); | ||
border-bottom-width: 0; | ||
|
||
&:first-of-type { | ||
border-top-right-radius: var(--tab-border-radius); | ||
} | ||
|
||
&:last-of-type { | ||
border-top-right-radius: 0; | ||
border-bottom-left-radius: var(--tab-border-radius); | ||
border-bottom-right-radius: var(--tab-border-radius); | ||
border-bottom-width: var(--tab-border-width); | ||
} | ||
|
||
&:not(:last-of-type) { | ||
border-right-width: var(--tab-border-width); | ||
border-bottom-width: 0; | ||
} | ||
|
||
&[aria-selected="true"] { | ||
border-bottom-color: var(--tab-border-color); | ||
box-shadow: inset calc(var(--tab-active-indicator-width) + var(--tab-border-width)) 0px 0px var(--tab-active-indicator-color); | ||
} | ||
} | ||
|
||
.tabs-panel { | ||
margin-block-start: var(--bloom-spacer-label); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import React from "react" | ||
|
||
import "./Tabs.scss" | ||
|
||
import { | ||
Tab as ReactTab, | ||
Tabs as ReactTabs, | ||
TabList as ReactTabList, | ||
TabPanel as ReactTabPanel, | ||
} from "react-tabs" | ||
|
||
export interface TabsProps { | ||
children: React.ReactNode | ||
className?: string | ||
defaultFocus?: boolean | ||
defaultIndex?: number | ||
disabledTabClassName?: string | ||
domRef?: (node?: HTMLElement) => void | ||
focusTabOnClick?: boolean | ||
forceRenderTabPanel?: boolean | ||
onSelect?: (index: number, last: number, event: Event) => boolean | void | ||
selectedIndex?: number | ||
selectedTabClassName?: string | ||
selectedTabPanelClassName?: string | ||
} | ||
|
||
const Tabs = (props: TabsProps) => { | ||
const className = ["tabs"] | ||
if (props.className) className.push(props.className) | ||
const focusTab = typeof props.focusTabOnClick !== "undefined" ? props.focusTabOnClick : false | ||
|
||
return ( | ||
<ReactTabs {...props} focusTabOnClick={focusTab} className={className.join(" ")} /> | ||
) | ||
} | ||
|
||
export interface TabProps { | ||
children: React.ReactNode | ||
className?: string | ||
disabled?: boolean | ||
disabledClassName?: string | ||
selectedClassName?: string | ||
tabIndex?: string | ||
} | ||
|
||
const Tab = (props: TabProps) => { | ||
const className = ["tabs-tab"] | ||
if (props.className) className.push(props.className) | ||
return ( | ||
<ReactTab selectedClassName="is-active" {...props} className={className} /> | ||
) | ||
} | ||
|
||
Tab.tabsRole = "Tab" | ||
|
||
export interface TabListProps { | ||
children: React.ReactNode | ||
size?: "sm" | "base" | ||
className?: string | ||
} | ||
|
||
const TabList = (props: TabListProps) => { | ||
const className = ["tabs-tablist"] | ||
if (props.className) className.push(props.className) | ||
|
||
return <ReactTabList data-size={props.size || "base"} className={className} children={props.children} /> | ||
} | ||
|
||
TabList.tabsRole = "TabList" | ||
|
||
export interface TabPanelProps { | ||
children: React.ReactNode | ||
className?: string | ||
forceRender?: boolean | ||
selectedClassName?: string | ||
} | ||
|
||
const TabPanel = (props: TabPanelProps) => { | ||
const className = ["tabs-panel"] | ||
if (props.className) className.push(props.className) | ||
return ( | ||
<ReactTabPanel selectedClassName="is-active" {...props} className={className} /> | ||
) | ||
} | ||
|
||
TabPanel.tabsRole = "TabPanel" | ||
|
||
Tabs.Tab = Tab | ||
Tabs.TabList = TabList | ||
Tabs.TabPanel = TabPanel | ||
|
||
export {Tabs as default, Tab, TabList, TabPanel} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { ArgsTable } from "@storybook/addon-docs" | ||
import Tabs from "../Tabs" | ||
import { Swatch } from "../../../documentation/components/Swatch.tsx" | ||
|
||
# <Tabs /> | ||
|
||
## Properties | ||
|
||
<ArgsTable of={Tabs} /> | ||
|
||
## Tab List Properties | ||
|
||
<ArgsTable of={Tabs.TabList} /> | ||
|
||
## Tab Properties | ||
|
||
<ArgsTable of={Tabs.Tab} /> | ||
|
||
## Tab Panel Properties | ||
|
||
<ArgsTable of={Tabs.TabPanel} /> | ||
|
||
## Theme Variables | ||
|
||
| Name | Description | Default | | ||
| ------------------------------ | ----------------------------------------- | ------------------------------ | | ||
| `--tab-font` | Font family | `--bloom-font-alt-sans` | | ||
| `--tab-font-size` | Font size | `--bloom-type-body` | | ||
| `--tab-font-size-sm` | Font size on mobile screens | `--bloom-type-label` | | ||
| `--tab-font-weight` | Font weight | `--bloom-font-weight-semibold` | | ||
| `--tab-font-weight-sm` | Font weight on mobile screens | `--bloom-font-weight-regular` | | ||
| `--tab-background-color` | <Swatch color="bloom-bg-color-surface" border={true} /> | `--bloom-bg-color-surface` | | ||
| `--tab-label-color` | <Swatch color="bloom-text-color-light" /> | `--bloom-text-color-light` | | ||
| `--tab-padding-inline` | Horizontal spacing within tab interior | `--bloom-s6` | | ||
| `--tab-padding-block` | Vertical spacing within tab interior | `--bloom-s4` | | ||
| `--tab-padding-inline-sm` | Horizontal spacing on mobile screens | `--bloom-s6` | | ||
| `--tab-padding-block-sm` | Vertical spacing on mobile screens | `--bloom-s3` | | ||
| `--tab-border-radius` | Tab border radius | `--bloom-rounded-lg` | | ||
| `--tab-border-color` | <Swatch color="bloom-border-color" /> | `--bloom-border-color` | | ||
| `--tab-border-width` | Tab border radius | `--bloom-border-1` | | ||
| `--tab-active-label-color` | <Swatch color="bloom-color-on-surface" /> | `--bloom-color-on-surface` | | ||
| `--tab-active-indicator-width` | Width of the colorful border | `--bloom-border-2` | | ||
| `--tab-active-indicator-color` | <Swatch color="bloom-color-primary" /> | `--bloom-color-primary` | | ||
| `--tab-panel-padding` | Spacing within the panel interior | `--bloom-spacer-section` | | ||
| `--tab-panel-background-color` | <Swatch color="bloom-bg-color-surface" border={true} /> | `--bloom-bg-color-surface` | |
Oops, something went wrong.