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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .changeset/legal-pigs-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@braid-design-system/docs-ui': minor
---

**TitleLink:** Added new component for rendering linkable headings with an optional copy-to-clipboard interaction. Should be wrapped in your required typographic component.

**EXAMPLE:**
```jsx
<Heading level="2">
<TitleLink>Getting started</TitleLink>
</Heading>
```

With copy-to-clipboard:
```jsx
<CategoryHeading component="h3">
<TitleLink copyable>Appearance</TitleLink>
</CategoryHeading>
```
16 changes: 16 additions & 0 deletions .changeset/red-drinks-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'braid-design-system': minor
---

---
updated:
- vars
---

**vars:** Exposed `vars.transition`. Transition CSS variables are available in stylesheets and runtime styles.

**EXAMPLE USAGE:**
```ts
export const myStyle = style({
transition: vars.transition.fast,
});
10 changes: 10 additions & 0 deletions .changeset/slick-ways-exist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@braid-design-system/docs-ui': minor
---

**CategoryHeading:** Added new component for rendering category-style navigation headings.

**EXAMPLE USAGE:**
```jsx
<CategoryHeading component="h2">Layout</CategoryHeading>
```
28 changes: 28 additions & 0 deletions .changeset/some-states-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
'braid-design-system': minor
---

---
updated:
- Badge
---

**Badge:** Added aria-hidden and aria-label props.

`aria-hidden` allows a badge to be hidden from assistive technology

`aria-label` allows visible badge text to be overridden with a more descriptive label for screen readers


**EXAMPLE USAGE:**
```jsx
<Badge aria-hidden>
Deprecated
</Badge>
```

```jsx
<Badge aria-label="You have 2 notifications">
2
</Badge>
```
2 changes: 2 additions & 0 deletions packages/braid-design-system/src/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const {
borderRadius,
borderWidth,
shadow,
transition,
} = internalVars;

const vars = {
Expand All @@ -38,6 +39,7 @@ const vars = {
borderRadius,
borderWidth,
shadow,
transition,
};

function atoms(props: Omit<Atoms, 'background'>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export interface BadgeProps {
data?: DataAttributeMap;
tabIndex?: BoxProps['tabIndex'];
'aria-describedby'?: string;
'aria-hidden'?: boolean;
'aria-label'?: string;
}

const lightModeBackgroundForTone = {
Expand Down Expand Up @@ -74,6 +76,8 @@ export const Badge = forwardRef<HTMLSpanElement, BadgeProps>(
data,
tabIndex,
'aria-describedby': ariaDescribedBy,
'aria-hidden': ariaHidden,
'aria-label': ariaLabel,
...restProps
},
ref,
Expand Down Expand Up @@ -112,6 +116,8 @@ export const Badge = forwardRef<HTMLSpanElement, BadgeProps>(
ref={ref}
tabIndex={tabIndex}
aria-describedby={ariaDescribedBy}
aria-hidden={ariaHidden}
aria-label={ariaLabel}
title={
title ??
(!ariaDescribedBy ? stringifyChildren(children) : undefined)
Expand Down
43 changes: 35 additions & 8 deletions packages/braid-design-system/src/lib/css/vars.docs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const Row = ({
hideCanvas?: boolean;
darkCanvas?: boolean;
}) => (
<Columns space="large" alignY="center">
<Columns space="large" alignY="center" collapseBelow="tablet">
<Column>
<Text>
<Hidden below="tablet">vars{group ? `.${group}` : null}.</Hidden>
Expand All @@ -49,16 +49,22 @@ const Row = ({
</Columns>
);

const ContentWidthValue = ({ var: varName }: { var: string }) => {
const [size, setSize] = useState(0);
const CssVarValue = ({
var: varName,
property,
}: {
var: string;
property: 'width' | 'transition';
}) => {
const [value, setValue] = useState('');
const { themeKey } = useThemeSettings();
const ref = useRef<HTMLDivElement | null>(null);

useEffect(() => {
if (ref.current && themeKey) {
setSize(ref.current.offsetWidth);
setValue(getComputedStyle(ref.current)[property]);
}
}, [themeKey]);
}, [themeKey, property]);

return (
<>
Expand All @@ -68,9 +74,9 @@ const ContentWidthValue = ({ var: varName }: { var: string }) => {
pointerEvents="none"
opacity={0}
ref={ref}
style={{ width: varName }}
style={{ [property]: varName } as any}
/>
{size > 0 ? `${size}px` : '&nbsp;'}
{value || '\u00a0'}
</>
);
};
Expand Down Expand Up @@ -106,7 +112,7 @@ const varDocs: Record<keyof typeof vars, ReactNodeNoStrings> = {
{index > 0 ? <Divider /> : null}
<Row key={widthName} group="contentWidth" name={widthName} hideCanvas>
<Text>
<ContentWidthValue var={widthVar} />
<CssVarValue var={widthVar} property="width" />
</Text>
</Row>
</Fragment>
Expand Down Expand Up @@ -283,6 +289,27 @@ const varDocs: Record<keyof typeof vars, ReactNodeNoStrings> = {
))}
</Stack>
),
transition: (
<Stack space="large">
{Object.entries(vars.transition).map(
([transitionName, transitionVar], index) => (
<Fragment key={transitionName}>
{index > 0 ? <Divider /> : null}
<Row
key={transitionName}
group="transition"
name={transitionName}
hideCanvas
>
<Text>
<CssVarValue var={transitionVar} property="transition" />
</Text>
</Row>
</Fragment>
),
)}
</Stack>
),
} as const;

const docs: CssDoc = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Box, Text } from 'braid-design-system';
import type { ComponentProps, ReactNode } from 'react';

type CategoryHeadingProps = {
children: ReactNode;
component?: ComponentProps<typeof Text>['component'];
};

export const CategoryHeading = ({
children,
component,
}: CategoryHeadingProps) => (
<Text size="xsmall" weight="medium" component={component}>
<Box style={{ textTransform: 'uppercase' }}>{children}</Box>
</Text>
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Box, HiddenVisually, Stack, Text } from 'braid-design-system';
import { Box, HiddenVisually, Stack } from 'braid-design-system';

import { CategoryHeading } from '../CategoryHeading/CategoryHeading';

import { SideNavigationItem } from './SideNavigationItem';

Expand All @@ -11,9 +13,7 @@ interface SideNavigationSection {
}

const Title = ({ children }: { children: string }) => (
<Text size="xsmall" weight="medium" component="h2">
{children}
</Text>
<CategoryHeading component="h2">{children}</CategoryHeading>
);

const ItemList = ({ items }: { items: SideNavigationItem[] }) => (
Expand Down
29 changes: 29 additions & 0 deletions packages/docs-ui/src/components/TitleLink/TitleLink.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { style } from '@vanilla-extract/css';
import { atoms, vars } from 'braid-design-system/css';

export const titleLink = style([
atoms({
display: 'flex',
borderRadius: 'small',
gap: 'xsmall',
alignItems: 'center',
}),
{
maxWidth: 'fit-content',
scrollMarginBlockStart: vars.space.small,
outlineOffset: vars.space.xsmall,
},
]);

export const isCopying = style({});

export const showIcon = style({
selectors: {
[`${titleLink}:hover &, ${titleLink}:focus-visible &, ${isCopying}&`]: {
opacity: 1,
},
[`${titleLink}:hover &`]: {
transition: vars.transition.fast,
},
},
});
97 changes: 97 additions & 0 deletions packages/docs-ui/src/components/TitleLink/TitleLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {
Box,
IconLink,
Link,
TooltipRenderer,
Text,
IconPositive,
} from 'braid-design-system';
import type { MouseEventHandler, ReactNode, RefCallback } from 'react';

import { useCopy } from '../../utils/useCopy';

import * as styles from './TitleLink.css';

const slugify = (string: string) =>
string
.replace(/[\s?]/g, '-')
.replace('--', '-')
.replace(/-$/, '')
.toLowerCase();

type TriggerProps = {
ref: RefCallback<HTMLElement>;
tabIndex: 0;
'aria-describedby': string;
};

const TitleLinkAnchor = ({
slug,
onClick,
children,
triggerProps,
copying,
}: {
slug: string;
onClick?: MouseEventHandler<HTMLAnchorElement>;
children: ReactNode;
triggerProps?: TriggerProps;
copying?: boolean;
}) => (
<Box component="span" id={slug} tabIndex={-1}>
<Link
href={`#${slug}`}
className={styles.titleLink}
onClick={onClick}
{...triggerProps}
>
{children}
<Box
component="span"
opacity={0}
className={[styles.showIcon, copying && styles.isCopying]}
>
{copying ? <IconPositive tone="positive" /> : <IconLink />}
</Box>
</Link>
</Box>
);

type TitleLink = { copyable?: boolean } & (
| { children: string }
| { children: ReactNode; label: string }
);

export const TitleLink = ({ copyable = false, ...restProps }: TitleLink) => {
const label = 'label' in restProps ? restProps.label : restProps.children;
const slug = slugify(label);
const { copying, onCopyClick } = useCopy();

const handleClick: MouseEventHandler<HTMLAnchorElement> = (event) => {
if (event.metaKey) {
return;
}
event.preventDefault();
const anchor = event.currentTarget as HTMLAnchorElement;
onCopyClick(anchor.href);
};

if (!copyable) {
return <TitleLinkAnchor slug={slug}>{restProps.children}</TitleLinkAnchor>;
}

return (
<TooltipRenderer placement="right" tooltip={<Text>Copy to clipboard</Text>}>
{({ triggerProps }) => (
<TitleLinkAnchor
slug={slug}
onClick={handleClick}
triggerProps={triggerProps}
copying={copying}
>
{restProps.children}
</TitleLinkAnchor>
)}
</TooltipRenderer>
);
};
2 changes: 2 additions & 0 deletions packages/docs-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ export {
KeyboardShortcut,
KeyboardIcon,
} from './components/KeyboardShortcut/KeyboardShortcut';
export { TitleLink } from './components/TitleLink/TitleLink';
export { CategoryHeading } from './components/CategoryHeading/CategoryHeading';
Loading
Loading