@guardian/stand 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1122 -1
- package/dist/avatar.cjs +9 -0
- package/dist/avatar.js +2 -0
- package/dist/button.cjs +9 -0
- package/dist/button.js +2 -0
- package/dist/components/TitleText.cjs +28 -0
- package/dist/components/TitleText.js +22 -0
- package/dist/components/avatar/Avatar.cjs +57 -0
- package/dist/components/avatar/Avatar.js +27 -0
- package/dist/components/avatar/styles.cjs +33 -0
- package/dist/components/avatar/styles.js +29 -0
- package/dist/components/avatar/types.cjs +16 -0
- package/dist/components/avatar/types.js +14 -0
- package/dist/components/button/Button.cjs +29 -0
- package/dist/components/button/Button.js +14 -0
- package/dist/components/button/styles.cjs +58 -0
- package/dist/components/button/styles.js +53 -0
- package/dist/components/icon/Icon.cjs +46 -0
- package/dist/components/icon/Icon.js +19 -0
- package/dist/components/icon/styles.cjs +27 -0
- package/dist/components/icon/styles.js +20 -0
- package/dist/components/link-button/LinkButton.cjs +29 -0
- package/dist/components/link-button/LinkButton.js +14 -0
- package/dist/components/link-button/styles.cjs +9 -0
- package/dist/components/link-button/styles.js +6 -0
- package/dist/components/typography/Typography.cjs +26 -0
- package/dist/components/typography/Typography.js +13 -0
- package/dist/components/typography/styles.cjs +15 -0
- package/dist/components/typography/styles.js +12 -0
- package/dist/components/user-menu/PreferenceRadioGroup.cjs +53 -0
- package/dist/components/user-menu/PreferenceRadioGroup.js +19 -0
- package/dist/components/user-menu/UserMenu.cjs +67 -0
- package/dist/components/user-menu/UserMenu.js +11 -0
- package/dist/components/user-menu/default-options.cjs +109 -0
- package/dist/components/user-menu/default-options.js +105 -0
- package/dist/components/user-menu/styles.cjs +90 -0
- package/dist/components/user-menu/styles.js +83 -0
- package/dist/fonts/MaterialSymbolsOutlined.css +23 -0
- package/dist/fonts/MaterialSymbolsRound.css +23 -0
- package/dist/fonts/MaterialSymbolsSharp.css +23 -0
- package/dist/fonts/material-symbols-types.ts +3825 -0
- package/dist/icon.cjs +9 -0
- package/dist/icon.js +2 -0
- package/dist/index.cjs +16 -4
- package/dist/index.js +6 -0
- package/dist/link-button.cjs +7 -0
- package/dist/link-button.js +1 -0
- package/dist/styleD/build/css/base/colors.css +1 -1
- package/dist/styleD/build/css/base/sizing.css +2 -0
- package/dist/styleD/build/css/component/avatar.css +54 -0
- package/dist/styleD/build/css/component/button.css +218 -0
- package/dist/styleD/build/css/component/byline.css +1 -1
- package/dist/styleD/build/css/component/icon.css +11 -0
- package/dist/styleD/build/css/component/typography.css +7 -0
- package/dist/styleD/build/css/component/userMenu.css +29 -0
- package/dist/styleD/build/css/semantic/colors.css +12 -8
- package/dist/styleD/build/css/semantic/sizing.css +1 -0
- package/dist/styleD/build/css/semantic/typography.css +3 -0
- package/dist/styleD/build/typescript/base/colors.cjs +1 -1
- package/dist/styleD/build/typescript/base/colors.js +1 -1
- package/dist/styleD/build/typescript/base/sizing.cjs +2 -0
- package/dist/styleD/build/typescript/base/sizing.js +2 -0
- package/dist/styleD/build/typescript/component/avatar.cjs +88 -0
- package/dist/styleD/build/typescript/component/avatar.js +86 -0
- package/dist/styleD/build/typescript/component/button.cjs +331 -0
- package/dist/styleD/build/typescript/component/button.js +329 -0
- package/dist/styleD/build/typescript/component/byline.cjs +1 -1
- package/dist/styleD/build/typescript/component/byline.js +1 -1
- package/dist/styleD/build/typescript/component/icon.cjs +19 -0
- package/dist/styleD/build/typescript/component/icon.js +17 -0
- package/dist/styleD/build/typescript/component/typography.cjs +7 -0
- package/dist/styleD/build/typescript/component/typography.js +5 -0
- package/dist/styleD/build/typescript/component/userMenu.cjs +37 -0
- package/dist/styleD/build/typescript/component/userMenu.js +35 -0
- package/dist/styleD/build/typescript/semantic/colors.cjs +14 -10
- package/dist/styleD/build/typescript/semantic/colors.js +14 -10
- package/dist/styleD/build/typescript/semantic/sizing.cjs +2 -1
- package/dist/styleD/build/typescript/semantic/sizing.js +2 -1
- package/dist/styleD/build/typescript/semantic/typography.cjs +5 -0
- package/dist/styleD/build/typescript/semantic/typography.js +5 -0
- package/dist/types/avatar.d.ts +19 -0
- package/dist/types/button.d.ts +20 -0
- package/dist/types/components/TitleText.d.ts +6 -0
- package/dist/types/components/avatar/Avatar.d.ts +2 -0
- package/dist/types/components/avatar/styles.d.ts +8 -0
- package/dist/types/components/avatar/types.d.ts +45 -0
- package/dist/types/components/button/Button.d.ts +2 -0
- package/dist/types/components/button/styles.d.ts +7 -0
- package/dist/types/components/button/types.d.ts +13 -0
- package/dist/types/components/byline/schema.d.ts +1 -1
- package/dist/types/components/icon/Icon.d.ts +2 -0
- package/dist/types/components/icon/styles.d.ts +8 -0
- package/dist/types/components/icon/types.d.ts +28 -0
- package/dist/types/components/link-button/LinkButton.d.ts +2 -0
- package/dist/types/components/link-button/styles.d.ts +330 -0
- package/dist/types/components/link-button/types.d.ts +13 -0
- package/dist/types/components/typography/Typography.d.ts +6 -0
- package/dist/types/components/typography/styles.d.ts +7 -0
- package/dist/types/components/typography/types.d.ts +13 -0
- package/dist/types/components/user-menu/PreferenceRadioGroup.d.ts +15 -0
- package/dist/types/components/user-menu/UserMenu.d.ts +17 -0
- package/dist/types/components/user-menu/default-options.d.ts +5 -0
- package/dist/types/components/user-menu/model.d.ts +9 -0
- package/dist/types/components/user-menu/styles.d.ts +9 -0
- package/dist/types/components/user-menu/theme.d.ts +3 -0
- package/dist/types/components/user-menu/types.d.ts +5 -0
- package/dist/types/fonts/material-symbols-types.d.ts +3822 -0
- package/dist/types/icon.d.ts +27 -0
- package/dist/types/index.d.ts +17 -0
- package/dist/types/link-button.d.ts +20 -0
- package/dist/types/styleD/build/typescript/base/colors.d.ts +1 -1
- package/dist/types/styleD/build/typescript/base/sizing.d.ts +2 -0
- package/dist/types/styleD/build/typescript/component/avatar.d.ts +88 -0
- package/dist/types/styleD/build/typescript/component/button.d.ts +331 -0
- package/dist/types/styleD/build/typescript/component/icon.d.ts +19 -0
- package/dist/types/styleD/build/typescript/component/typography.d.ts +7 -0
- package/dist/types/styleD/build/typescript/component/userMenu.d.ts +37 -0
- package/dist/types/styleD/build/typescript/semantic/colors.d.ts +7 -3
- package/dist/types/styleD/build/typescript/semantic/sizing.d.ts +1 -0
- package/dist/types/styleD/build/typescript/semantic/typography.d.ts +5 -0
- package/dist/types/typography.d.ts +19 -0
- package/dist/types/user-menu.d.ts +18 -0
- package/dist/types/util/reset.d.ts +1 -0
- package/dist/types/util/storybookStyles.d.ts +1 -0
- package/dist/types/util/types.d.ts +28 -0
- package/dist/types/utils.d.ts +1 -0
- package/dist/typography.cjs +9 -0
- package/dist/typography.js +2 -0
- package/dist/user-menu.cjs +9 -0
- package/dist/user-menu.js +2 -0
- package/dist/util/css/reset.css +124 -0
- package/dist/util/reset.cjs +10 -0
- package/dist/util/reset.css.cjs +5 -0
- package/dist/util/reset.css.js +3 -0
- package/dist/util/reset.js +8 -0
- package/dist/utils.cjs +2 -0
- package/dist/utils.js +1 -0
- package/package.json +65 -3
package/README.md
CHANGED
|
@@ -453,7 +453,1048 @@ For a list of the available base/primitives radius tokens see the [Storybook Bas
|
|
|
453
453
|
|
|
454
454
|
For a full list of CSS Base/Primitives Radius tokens see [`base/radius.css`](./src/styleD/build/css/base/radius.css).
|
|
455
455
|
|
|
456
|
-
## Components
|
|
456
|
+
## Components - Base
|
|
457
|
+
|
|
458
|
+
General purpose components for use across a variety of editorial tools based on the design system.
|
|
459
|
+
|
|
460
|
+
### `Avatar`
|
|
461
|
+
|
|
462
|
+
The Avatar component displays a user's profile image or initials in a circular container. It supports multiple sizes, colors, and automatic fallback handling.
|
|
463
|
+
|
|
464
|
+
**When to use**
|
|
465
|
+
|
|
466
|
+
- Display user profiles in lists, comments, or headers
|
|
467
|
+
- Show user identity in messaging interfaces
|
|
468
|
+
- Represent users in collaborative features
|
|
469
|
+
|
|
470
|
+
**Peer dependencies:**
|
|
471
|
+
|
|
472
|
+
- `@emotion/react`
|
|
473
|
+
- `react`
|
|
474
|
+
- `react-dom`
|
|
475
|
+
- `typescript`
|
|
476
|
+
|
|
477
|
+
See the `peerDependencies` section of `package.json` for compatible versions.
|
|
478
|
+
|
|
479
|
+
See [avatar custom component build](#avatar-custom-component-build) for usage without React/Emotion.
|
|
480
|
+
|
|
481
|
+
#### Example usage
|
|
482
|
+
|
|
483
|
+
See "Emotion/React" heading on [codesandbox.io](https://codesandbox.io/p/sandbox/guardian-stand-avatar-component-mqvzh5)
|
|
484
|
+
|
|
485
|
+
```tsx
|
|
486
|
+
import { Avatar } from '@guardian/stand/avatar';
|
|
487
|
+
|
|
488
|
+
/* types, if required */
|
|
489
|
+
import type { AvatarProps, AvatarTheme } from '@guardian/stand/avatar';
|
|
490
|
+
|
|
491
|
+
/* Initials only */
|
|
492
|
+
<Avatar initials="AB" size="md" />
|
|
493
|
+
|
|
494
|
+
/* Image with alt text */
|
|
495
|
+
<Avatar
|
|
496
|
+
src="https://example.com/avatar.jpg"
|
|
497
|
+
alt="User Name"
|
|
498
|
+
size="md"
|
|
499
|
+
/>
|
|
500
|
+
|
|
501
|
+
/* Image with fallback initials and a specific color */
|
|
502
|
+
<Avatar
|
|
503
|
+
src="https://example.com/avatar.jpg"
|
|
504
|
+
alt="User Name"
|
|
505
|
+
initials="AB"
|
|
506
|
+
color="blue"
|
|
507
|
+
size="md"
|
|
508
|
+
/>
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
#### Props
|
|
512
|
+
|
|
513
|
+
| Name | Type | Required | Default | Description |
|
|
514
|
+
| -------------- | ------------------ | ----------- | ------------------------------- | ---------------------------------------------- |
|
|
515
|
+
| `size` | `'sm'` \| `'md'` | No | `'md'` | Size of the avatar. |
|
|
516
|
+
| `color` | Various | No | Deterministic based on initials | Color of the avatar. |
|
|
517
|
+
| `initials` | `string` | Conditional | N/A | Initials to display when no image is provided. |
|
|
518
|
+
| `src` | `string` | Conditional | N/A | URL of the avatar image. |
|
|
519
|
+
| `alt` | `string` | Conditional | N/A | Alt text for the image for accessibility. |
|
|
520
|
+
| `theme` | `AvatarTheme` | No | N/A | Custom theme overrides for the avatar. |
|
|
521
|
+
| `cssOverrides` | `SerializedStyles` | No | N/A | Custom CSS styles for the avatar. |
|
|
522
|
+
| `className` | `string` | No | N/A | Additional class name(s) for the avatar. |
|
|
523
|
+
|
|
524
|
+
When using `src`, the `alt` prop is required for accessibility.
|
|
525
|
+
When not using `src`, the `initials` prop is required.
|
|
526
|
+
|
|
527
|
+
**`size`**
|
|
528
|
+
|
|
529
|
+
The avatar supports two sizes:
|
|
530
|
+
|
|
531
|
+
- `sm` (small): 32px
|
|
532
|
+
- `md` (medium): 40px
|
|
533
|
+
|
|
534
|
+
**`color`**
|
|
535
|
+
|
|
536
|
+
When no `color` is set, the avatar picks a deterministic color based on the initials (with the exception of `outlined`). You can also specify a color explicitly.
|
|
537
|
+
|
|
538
|
+
Available colors: `outlined`, `blue`, `green`, `red`, `cyan`, `teal`, `cool-purple`, `warm-purple`, `magenta`, `orange`, `yellow`
|
|
539
|
+
|
|
540
|
+
#### Customisation
|
|
541
|
+
|
|
542
|
+
We recommend using the Avatar component as provided, but it can be customised using the `theme` or `cssOverrides` props as required.
|
|
543
|
+
|
|
544
|
+
**Custom theme**
|
|
545
|
+
|
|
546
|
+
The `theme` prop allows you to override specific design tokens for the Avatar component:
|
|
547
|
+
|
|
548
|
+
```tsx
|
|
549
|
+
import type { AvatarTheme } from '@guardian/stand/avatar';
|
|
550
|
+
import { Avatar } from '@guardian/stand/avatar';
|
|
551
|
+
import { baseColors, baseSizing } from '@guardian/stand';
|
|
552
|
+
|
|
553
|
+
const customTheme: AvatarTheme = {
|
|
554
|
+
shared: {
|
|
555
|
+
color: {
|
|
556
|
+
blue: {
|
|
557
|
+
background: baseColors.blue[100],
|
|
558
|
+
text: baseColors.neutral[900],
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
md: {
|
|
563
|
+
size: baseSizing['size-48-rem'],
|
|
564
|
+
},
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
const Component = () => (
|
|
568
|
+
<Avatar color="blue" initials="CT" size="md" theme={customTheme} />
|
|
569
|
+
);
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**CSS overrides**
|
|
573
|
+
|
|
574
|
+
The `cssOverrides` prop allows you to pass custom CSS to the Avatar component, if the theme prop is not sufficient:
|
|
575
|
+
|
|
576
|
+
```tsx
|
|
577
|
+
import { Avatar } from '@guardian/stand/avatar';
|
|
578
|
+
import { baseColors, baseSizing, baseSpacing } from '@guardian/stand';
|
|
579
|
+
import { css } from '@emotion/react';
|
|
580
|
+
|
|
581
|
+
const customStyles = css`
|
|
582
|
+
border: ${baseSizing['size-2-rem']} solid ${baseColors.red[500]};
|
|
583
|
+
margin: ${baseSpacing['8-rem']};
|
|
584
|
+
`;
|
|
585
|
+
|
|
586
|
+
const Component = () => (
|
|
587
|
+
<Avatar initials="CO" size="md" color="red" cssOverrides={customStyles} />
|
|
588
|
+
);
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
#### Avatar Custom Component Build
|
|
592
|
+
|
|
593
|
+
If you're not using React/Emotion, or `@guardian/stand` is not compatible with your project, you can create a custom Avatar component using the styles defined in the `AvatarTheme` type.
|
|
594
|
+
|
|
595
|
+
You will however be responsible for any additional functionality on top of the styles, for example image loading, image fallback, accessibility etc.
|
|
596
|
+
|
|
597
|
+
See "Custom Component" heading on [codesandbox.io](https://codesandbox.io/p/sandbox/guardian-stand-avatar-component-mqvzh5)
|
|
598
|
+
|
|
599
|
+
**`css`**
|
|
600
|
+
|
|
601
|
+
You can import the Avatar styles as CSS from the package, make sure that your build process supports importing CSS from `node_modules`/`package.json`:
|
|
602
|
+
|
|
603
|
+
```css
|
|
604
|
+
/* import the font and avatar styles */
|
|
605
|
+
@import '@guardian/stand/fonts/OpenSans.css';
|
|
606
|
+
@import '@guardian/stand/component/avatar.css';
|
|
607
|
+
|
|
608
|
+
/*
|
|
609
|
+
or for scenarios where you have to use relative paths/node_modules directly:
|
|
610
|
+
|
|
611
|
+
@import 'node_modules/@guardian/stand/dist/fonts/OpenSans.css';
|
|
612
|
+
@import 'node_modules/@guardian/stand/dist/styleD/build/css/component/avatar.css';
|
|
613
|
+
*/
|
|
614
|
+
|
|
615
|
+
/* example setup of avatar style using md size and blue color */
|
|
616
|
+
.stand-avatar {
|
|
617
|
+
display: var(--component-avatar-shared-display);
|
|
618
|
+
align-items: var(--component-avatar-shared-align-items);
|
|
619
|
+
justify-content: var(--component-avatar-shared-justify-content);
|
|
620
|
+
overflow: var(--component-avatar-shared-overflow);
|
|
621
|
+
flex-shrink: var(--component-avatar-shared-flex-shrink);
|
|
622
|
+
border-radius: var(--component-avatar-shared-border-radius);
|
|
623
|
+
background-color: var(--component-avatar-shared-color-blue-background);
|
|
624
|
+
width: var(--component-avatar-md-size);
|
|
625
|
+
height: var(--component-avatar-md-size);
|
|
626
|
+
border: var(--component-avatar-shared-color-blue-border);
|
|
627
|
+
color: var(--component-avatar-shared-color-blue-text);
|
|
628
|
+
font: var(--component-avatar-md-typography-font);
|
|
629
|
+
letter-spacing: var(--component-avatar-md-typography-letter-spacing);
|
|
630
|
+
font-variation-settings: 'wdth'
|
|
631
|
+
var(--component-avatar-md-typography-font-width);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/* example setup for avatar image */
|
|
635
|
+
.stand-avatar > img {
|
|
636
|
+
width: 100%;
|
|
637
|
+
height: 100%;
|
|
638
|
+
object-fit: cover;
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
```html
|
|
643
|
+
<!-- example usage of avatar style in html with avatar -->
|
|
644
|
+
<div class="stand-avatar">AB</div>
|
|
645
|
+
|
|
646
|
+
<!-- example usage of avatar style in html with avatar image -->
|
|
647
|
+
<div class="stand-avatar">
|
|
648
|
+
<img src="https://example.com/avatar.jpg" alt="User Name" />
|
|
649
|
+
</div>
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
**TypeScript/JavaScript**
|
|
653
|
+
|
|
654
|
+
Use the `componentAvatar` variable and the `ComponentAvatar` type to define your custom styles in TypeScript/JavaScript:
|
|
655
|
+
|
|
656
|
+
```ts
|
|
657
|
+
import type { ComponentAvatar } from '@guardian/stand'; // if types required
|
|
658
|
+
import { componentAvatar } from '@guardian/stand';
|
|
659
|
+
|
|
660
|
+
const style = `
|
|
661
|
+
display: ${componentAvatar.shared.display};
|
|
662
|
+
align-items: ${componentAvatar.shared['align-items']};
|
|
663
|
+
justify-content: ${componentAvatar.shared['justify-content']};
|
|
664
|
+
overflow: ${componentAvatar.shared.overflow};
|
|
665
|
+
flex-shrink: ${componentAvatar.shared['flex-shrink']};
|
|
666
|
+
border-radius: ${componentAvatar.shared['border-radius']};
|
|
667
|
+
background-color: ${componentAvatar.shared.color.blue.background};
|
|
668
|
+
width: ${componentAvatar.md.size};
|
|
669
|
+
height: ${componentAvatar.md.size};
|
|
670
|
+
border: ${componentAvatar.shared.color.blue.border};
|
|
671
|
+
color: ${componentAvatar.shared.color.blue.text};
|
|
672
|
+
font: ${componentAvatar.md.typography.font};
|
|
673
|
+
letter-spacing: ${componentAvatar.md.typography.letterSpacing};
|
|
674
|
+
font-variation-settings: 'wdth' ${componentAvatar.md.typography.fontWidth};
|
|
675
|
+
`;
|
|
676
|
+
|
|
677
|
+
const imgStyle = `
|
|
678
|
+
width: 100%;
|
|
679
|
+
height: 100%;
|
|
680
|
+
object-fit: cover;
|
|
681
|
+
`;
|
|
682
|
+
|
|
683
|
+
// e.g. adding to DOM using vanilla JS styles
|
|
684
|
+
document.getElementById('app')!.innerHTML = `
|
|
685
|
+
<h2>
|
|
686
|
+
Using <code>typescript</code>/<code>javascript</code>
|
|
687
|
+
</h2>
|
|
688
|
+
<div style="${style}">AB</div>
|
|
689
|
+
<div style="${style}">
|
|
690
|
+
<img
|
|
691
|
+
style="${imgStyle}"
|
|
692
|
+
src="https://example.com/avatar.jpg"
|
|
693
|
+
alt="User Name"
|
|
694
|
+
/>
|
|
695
|
+
</div>
|
|
696
|
+
`;
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### `Button`
|
|
700
|
+
|
|
701
|
+
A React Aria powered button that follows Stand design and accessibility defaults. It supports multiple visual variants, four sizes, disabled state handling, and render props for pending interactions.
|
|
702
|
+
|
|
703
|
+
**When to use**
|
|
704
|
+
|
|
705
|
+
- Primary and secondary actions that require clear affordance
|
|
706
|
+
- Form submissions or confirm/cancel flows
|
|
707
|
+
- Re-usable button styles that align with Stand tokens
|
|
708
|
+
|
|
709
|
+
**Peer dependencies:**
|
|
710
|
+
|
|
711
|
+
- `@emotion/react`
|
|
712
|
+
- `react`
|
|
713
|
+
- `react-dom`
|
|
714
|
+
- `react-aria-components`
|
|
715
|
+
- `typescript`
|
|
716
|
+
|
|
717
|
+
See the `peerDependencies` section of `package.json` for compatible versions.
|
|
718
|
+
|
|
719
|
+
See [button custom component build](#button-custom-component-build) for usage without React/Emotion.
|
|
720
|
+
|
|
721
|
+
#### Example usage
|
|
722
|
+
|
|
723
|
+
See "Emotion/React" heading on [codesandbox.io](https://codesandbox.io/p/sandbox/jctg2k)
|
|
724
|
+
|
|
725
|
+
```tsx
|
|
726
|
+
import { Button } from '@guardian/stand/button';
|
|
727
|
+
import type { ButtonProps, ButtonTheme } from '@guardian/stand/button';
|
|
728
|
+
|
|
729
|
+
const Example = () => (
|
|
730
|
+
<>
|
|
731
|
+
<Button
|
|
732
|
+
variant="emphasised-primary"
|
|
733
|
+
size="md"
|
|
734
|
+
onPress={() => {
|
|
735
|
+
console.log('Primary action');
|
|
736
|
+
}}
|
|
737
|
+
>
|
|
738
|
+
Publish
|
|
739
|
+
</Button>
|
|
740
|
+
|
|
741
|
+
<Button variant="neutral-secondary" size="sm" isDisabled>
|
|
742
|
+
Disabled
|
|
743
|
+
</Button>
|
|
744
|
+
</>
|
|
745
|
+
);
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
#### Props
|
|
749
|
+
|
|
750
|
+
| Name | Type | Required | Default | Description |
|
|
751
|
+
| -------------- | -------------------------------------------------------------------------------------------------- | -------- | ---------------------- | ------------------------------------------------------------------------------------------------- |
|
|
752
|
+
| `size` | `'xs'` \| `'sm'` \| `'md'` \| `'lg'` | No | `'md'` | Controls the button dimensions and typography. |
|
|
753
|
+
| `variant` | `'emphasised-primary'` \| `'emphasised-secondary'` \| `'neutral-primary'` \| `'neutral-secondary'` | No | `'emphasised-primary'` | Chooses the colour scheme and interaction states. |
|
|
754
|
+
| `children` | `ReactNode` \| `RenderProps` | Yes | N/A | Content inside the button. Render props receive `{ isPending }` from React Aria. |
|
|
755
|
+
| `isDisabled` | `boolean` | No | `false` | Disables interaction and applies disabled styling. |
|
|
756
|
+
| `theme` | `ButtonTheme` (partial) | No | N/A | Override Stand tokens for this instance; merged over the default theme. |
|
|
757
|
+
| `cssOverrides` | `SerializedStyles` \| `SerializedStyles[]` | No | N/A | Low-level escape hatch for Emotion overrides when theming is insufficient. |
|
|
758
|
+
| `className` | `string` | No | N/A | Optional class name forwarded to the root React Aria button. |
|
|
759
|
+
| `...props` | `ReactAria Button` props | No | N/A | All other props from `react-aria-components` `Button` (e.g. `type`, `autoFocus`, event handlers). |
|
|
760
|
+
|
|
761
|
+
**`size` and `variant`**
|
|
762
|
+
|
|
763
|
+
- Sizes: `xs`, `sm`, `md`, `lg`
|
|
764
|
+
- Variants: `emphasised-primary`, `emphasised-secondary`, `neutral-primary`, `neutral-secondary`
|
|
765
|
+
|
|
766
|
+
#### Customisation
|
|
767
|
+
|
|
768
|
+
We recommend using the default theme. When needed, use the `theme` or `cssOverrides` props.
|
|
769
|
+
|
|
770
|
+
**Custom theme**
|
|
771
|
+
|
|
772
|
+
```tsx
|
|
773
|
+
import type { ButtonTheme } from '@guardian/stand/button';
|
|
774
|
+
import { Button } from '@guardian/stand/button';
|
|
775
|
+
import { baseColors } from '@guardian/stand';
|
|
776
|
+
|
|
777
|
+
const customTheme: Partial<ButtonTheme> = {
|
|
778
|
+
'emphasised-primary': {
|
|
779
|
+
shared: {
|
|
780
|
+
backgroundColor: baseColors['cool-purple'][200],
|
|
781
|
+
color: baseColors['cool-purple'][900],
|
|
782
|
+
border: `2px solid ${baseColors['cool-purple'][700]}`,
|
|
783
|
+
':hover': {
|
|
784
|
+
backgroundColor: baseColors['cool-purple'][300],
|
|
785
|
+
border: `2px solid ${baseColors['cool-purple'][700]}`,
|
|
786
|
+
},
|
|
787
|
+
':active': {
|
|
788
|
+
backgroundColor: baseColors['cool-purple'][400],
|
|
789
|
+
border: `2px solid ${baseColors['cool-purple'][700]}`,
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
},
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
const Component = () => (
|
|
796
|
+
<Button variant="emphasised-primary" size="md" theme={customTheme}>
|
|
797
|
+
Custom Themed Button
|
|
798
|
+
</Button>
|
|
799
|
+
);
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
**CSS overrides**
|
|
803
|
+
|
|
804
|
+
```tsx
|
|
805
|
+
import { Button } from '@guardian/stand/button';
|
|
806
|
+
import { baseSpacing } from '@guardian/stand';
|
|
807
|
+
import { css } from '@emotion/react';
|
|
808
|
+
|
|
809
|
+
const customStyles = css`
|
|
810
|
+
width: 100%;
|
|
811
|
+
text-transform: full-width;
|
|
812
|
+
font-variant: small-caps;
|
|
813
|
+
padding-inline: ${baseSpacing['24-rem']};
|
|
814
|
+
`;
|
|
815
|
+
|
|
816
|
+
const Component = () => (
|
|
817
|
+
<Button variant="neutral-primary" size="sm" cssOverrides={customStyles}>
|
|
818
|
+
CSSOverrides Button
|
|
819
|
+
</Button>
|
|
820
|
+
);
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
#### Button Custom Component Build
|
|
824
|
+
|
|
825
|
+
If you're not using React/Emotion, or `@guardian/stand` is not compatible with your project, you can create a custom `Button`/`LinkButton` component using the styles defined in the `ButtonTheme` type.
|
|
826
|
+
|
|
827
|
+
You will however be responsible for any additional functionality on top of the styles, for example accessibility, focus management, interaction states etc.
|
|
828
|
+
|
|
829
|
+
See "Custom Component" heading on [codesandbox.io](https://codesandbox.io/p/sandbox/jctg2k)
|
|
830
|
+
|
|
831
|
+
**`css`**
|
|
832
|
+
|
|
833
|
+
You can import the Button styles as CSS from the package, make sure that your build process supports importing CSS from `node_modules`/`package.json`:
|
|
834
|
+
|
|
835
|
+
```css
|
|
836
|
+
/* import the font and button variables */
|
|
837
|
+
@import '@guardian/stand/fonts/OpenSans.css';
|
|
838
|
+
@import '@guardian/stand/component/button.css';
|
|
839
|
+
|
|
840
|
+
/*
|
|
841
|
+
or for scenarios where you have to use relative paths/node_modules directly:
|
|
842
|
+
|
|
843
|
+
@import 'node_modules/@guardian/stand/dist/fonts/OpenSans.css';
|
|
844
|
+
@import 'node_modules/@guardian/stand/dist/styleD/build/css/component/button.css';
|
|
845
|
+
*/
|
|
846
|
+
|
|
847
|
+
/* shared button styles for all variants */
|
|
848
|
+
.stand-button {
|
|
849
|
+
display: var(--component-button-shared-display);
|
|
850
|
+
-webkit-appearance: var(--component-button-shared-webkit-appearance);
|
|
851
|
+
text-align: var(--component-button-shared-text-align);
|
|
852
|
+
box-shadow: var(--component-button-shared-box-shadow);
|
|
853
|
+
text-decoration: var(--component-button-shared-text-decoration);
|
|
854
|
+
cursor: var(--component-button-shared-cursor);
|
|
855
|
+
justify-content: var(--component-button-shared-justify-content);
|
|
856
|
+
align-items: var(--component-button-shared-align-items);
|
|
857
|
+
}
|
|
858
|
+
.stand-button:focus-visible {
|
|
859
|
+
outline: var(--component-button-shared-focus-visible-outline);
|
|
860
|
+
outline-offset: var(--component-button-shared-focus-visible-outline-offset);
|
|
861
|
+
}
|
|
862
|
+
.stand-button:disabled {
|
|
863
|
+
cursor: var(--component-button-shared-disabled-cursor);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/* example setup of button/link button style using md size and emphasised primary variant */
|
|
867
|
+
.stand-button-emphasised-primary {
|
|
868
|
+
color: var(--component-button-emphasised-primary-shared-color);
|
|
869
|
+
background: var(
|
|
870
|
+
--component-button-emphasised-primary-shared-background-color
|
|
871
|
+
);
|
|
872
|
+
height: var(--component-button-emphasised-primary-md-height);
|
|
873
|
+
padding: var(--component-button-emphasised-primary-md-padding-top)
|
|
874
|
+
var(--component-button-emphasised-primary-md-padding-right)
|
|
875
|
+
var(--component-button-emphasised-primary-md-padding-bottom)
|
|
876
|
+
var(--component-button-emphasised-primary-md-padding-left);
|
|
877
|
+
font: var(--component-button-emphasised-primary-md-typography-font);
|
|
878
|
+
letter-spacing: var(
|
|
879
|
+
--component-button-emphasised-primary-md-typography-letter-spacing
|
|
880
|
+
);
|
|
881
|
+
font-variation-settings: 'wdth'
|
|
882
|
+
var(--component-button-emphasised-primary-md-typography-font-width);
|
|
883
|
+
border: var(--component-button-emphasised-primary-shared-border);
|
|
884
|
+
border-radius: var(
|
|
885
|
+
--component-button-emphasised-primary-shared-border-radius
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
.stand-button-emphasised-primary:hover {
|
|
889
|
+
background: var(
|
|
890
|
+
--component-button-emphasised-primary-shared-hover-background-color
|
|
891
|
+
);
|
|
892
|
+
border: var(--component-button-emphasised-primary-shared-hover-border);
|
|
893
|
+
}
|
|
894
|
+
.stand-button-emphasised-primary:active {
|
|
895
|
+
background: var(
|
|
896
|
+
--component-button-emphasised-primary-shared-active-background-color
|
|
897
|
+
);
|
|
898
|
+
border: var(--component-button-emphasised-primary-shared-active-border);
|
|
899
|
+
}
|
|
900
|
+
.stand-button-emphasised-primary:disabled {
|
|
901
|
+
color: var(--component-button-emphasised-primary-shared-disabled-color);
|
|
902
|
+
background: var(
|
|
903
|
+
--component-button-emphasised-primary-shared-disabled-background-color
|
|
904
|
+
);
|
|
905
|
+
border: var(--component-button-emphasised-primary-shared-disabled-border);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/* example setup of button/link button style using md size and neutral secondary variant */
|
|
909
|
+
.stand-button-neutral-secondary {
|
|
910
|
+
color: var(--component-button-neutral-secondary-shared-color);
|
|
911
|
+
background: var(
|
|
912
|
+
--component-button-neutral-secondary-shared-background-color
|
|
913
|
+
);
|
|
914
|
+
height: var(--component-button-neutral-secondary-md-height);
|
|
915
|
+
padding: var(--component-button-neutral-secondary-md-padding-top)
|
|
916
|
+
var(--component-button-neutral-secondary-md-padding-right)
|
|
917
|
+
var(--component-button-neutral-secondary-md-padding-bottom)
|
|
918
|
+
var(--component-button-neutral-secondary-md-padding-left);
|
|
919
|
+
font: var(--component-button-neutral-secondary-md-typography-font);
|
|
920
|
+
letter-spacing: var(
|
|
921
|
+
--component-button-neutral-secondary-md-typography-letter-spacing
|
|
922
|
+
);
|
|
923
|
+
font-variation-settings: 'wdth'
|
|
924
|
+
var(--component-button-neutral-secondary-md-typography-font-width);
|
|
925
|
+
border: var(--component-button-neutral-secondary-shared-border);
|
|
926
|
+
border-radius: var(
|
|
927
|
+
--component-button-neutral-secondary-shared-border-radius
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
.stand-button-neutral-secondary:hover {
|
|
931
|
+
background: var(
|
|
932
|
+
--component-button-neutral-secondary-shared-hover-background-color
|
|
933
|
+
);
|
|
934
|
+
border: var(--component-button-neutral-secondary-shared-hover-border);
|
|
935
|
+
}
|
|
936
|
+
.stand-button-neutral-secondary:active {
|
|
937
|
+
background: var(
|
|
938
|
+
--component-button-neutral-secondary-shared-active-background-color
|
|
939
|
+
);
|
|
940
|
+
border: var(--component-button-neutral-secondary-shared-active-border);
|
|
941
|
+
}
|
|
942
|
+
.stand-button-neutral-secondary:disabled {
|
|
943
|
+
color: var(--component-button-neutral-secondary-shared-disabled-color);
|
|
944
|
+
background: var(
|
|
945
|
+
--component-button-neutral-secondary-shared-disabled-background-color
|
|
946
|
+
);
|
|
947
|
+
border: var(--component-button-neutral-secondary-shared-disabled-border);
|
|
948
|
+
}
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
```html
|
|
952
|
+
<button class="stand-button stand-button-emphasised-primary">
|
|
953
|
+
Button Label
|
|
954
|
+
</button>
|
|
955
|
+
<button class="stand-button stand-button-emphasised-primary" disabled>
|
|
956
|
+
Disabled Button Label
|
|
957
|
+
</button>
|
|
958
|
+
<a class="stand-button stand-button-neutral-secondary" href="#"
|
|
959
|
+
>LinkButton Label</a
|
|
960
|
+
>
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
**TypeScript/JavaScript**
|
|
964
|
+
|
|
965
|
+
Use the `componentButton` variable and the `ComponentButton` type to define your custom styles in TypeScript/JavaScript:
|
|
966
|
+
|
|
967
|
+
```ts
|
|
968
|
+
import type { ComponentButton } from '@guardian/stand'; // if types required
|
|
969
|
+
import { componentButton } from '@guardian/stand/button';
|
|
970
|
+
|
|
971
|
+
/* NB: The HTML style attribute cannot target psuedo selectors, so they haven't been implemented e.g. hover/focus */
|
|
972
|
+
const sharedButtonStyles = `
|
|
973
|
+
display: ${componentButton.shared.display};
|
|
974
|
+
-webkit-appearance: ${componentButton.shared['-webkit-appearance']};
|
|
975
|
+
text-align: ${componentButton.shared['text-align']};
|
|
976
|
+
box-shadow: ${componentButton.shared['box-shadow']};
|
|
977
|
+
text-decoration: ${componentButton.shared['text-decoration']};
|
|
978
|
+
cursor: ${componentButton.shared.cursor};
|
|
979
|
+
justify-content: ${componentButton.shared['justify-content']};
|
|
980
|
+
align-items: ${componentButton.shared['align-items']};
|
|
981
|
+
`;
|
|
982
|
+
|
|
983
|
+
const emphasisedPrimaryButtonStyles = `
|
|
984
|
+
${sharedButtonStyles}
|
|
985
|
+
color: ${componentButton['emphasised-primary'].shared.color};
|
|
986
|
+
background: ${componentButton['emphasised-primary'].shared.backgroundColor};
|
|
987
|
+
height: ${componentButton['emphasised-primary'].md.height};
|
|
988
|
+
padding: ${componentButton['emphasised-primary'].md.padding.top}
|
|
989
|
+
${componentButton['emphasised-primary'].md.padding.right}
|
|
990
|
+
${componentButton['emphasised-primary'].md.padding.bottom}
|
|
991
|
+
${componentButton['emphasised-primary'].md.padding.left};
|
|
992
|
+
font: ${componentButton['emphasised-primary'].md.typography.font};
|
|
993
|
+
letter-spacing: ${componentButton['emphasised-primary'].md.typography.letterSpacing};
|
|
994
|
+
font-variation-settings: 'wdth'
|
|
995
|
+
${componentButton['emphasised-primary'].md.typography.fontWidth};
|
|
996
|
+
border: ${componentButton['emphasised-primary'].shared.border};
|
|
997
|
+
border-radius: ${componentButton['emphasised-primary'].shared.borderRadius};
|
|
998
|
+
`;
|
|
999
|
+
|
|
1000
|
+
const emphasisedPrimaryButtonDisabledStyles = `
|
|
1001
|
+
${emphasisedPrimaryButtonStyles}
|
|
1002
|
+
cursor: ${componentButton.shared[':disabled'].cursor};
|
|
1003
|
+
color: ${componentButton['emphasised-primary'].shared[':disabled'].color};
|
|
1004
|
+
background: ${componentButton['emphasised-primary'].shared[':disabled'].backgroundColor};
|
|
1005
|
+
border: ${componentButton['emphasised-primary'].shared[':disabled'].border};
|
|
1006
|
+
`;
|
|
1007
|
+
|
|
1008
|
+
const neutralSecondaryButtonStyles = `
|
|
1009
|
+
${sharedButtonStyles}
|
|
1010
|
+
color: ${componentButton['neutral-secondary'].shared.color};
|
|
1011
|
+
background: ${componentButton['neutral-secondary'].shared.backgroundColor};
|
|
1012
|
+
height: ${componentButton['neutral-secondary'].md.height};
|
|
1013
|
+
padding: ${componentButton['neutral-secondary'].md.padding.top}
|
|
1014
|
+
${componentButton['neutral-secondary'].md.padding.right}
|
|
1015
|
+
${componentButton['neutral-secondary'].md.padding.bottom}
|
|
1016
|
+
${componentButton['neutral-secondary'].md.padding.left};
|
|
1017
|
+
font: ${componentButton['neutral-secondary'].md.typography.font};
|
|
1018
|
+
letter-spacing: ${componentButton['neutral-secondary'].md.typography.letterSpacing};
|
|
1019
|
+
font-variation-settings: 'wdth'
|
|
1020
|
+
${componentButton['neutral-secondary'].md.typography.fontWidth};
|
|
1021
|
+
border: ${componentButton['neutral-secondary'].shared.border};
|
|
1022
|
+
border-radius: ${componentButton['neutral-secondary'].shared.borderRadius};
|
|
1023
|
+
`;
|
|
1024
|
+
|
|
1025
|
+
// e.g. adding to DOM using vanilla JS styles
|
|
1026
|
+
document.getElementById('app')!.innerHTML = `
|
|
1027
|
+
<button style="${emphasisedPrimaryButtonStyles}">Button Label</button>
|
|
1028
|
+
<button disabled style="${emphasisedPrimaryButtonDisabledStyles}">Disabled Button Label</button>
|
|
1029
|
+
<a href="#" style="${neutralSecondaryButtonStyles}">LinkButton Label</a>
|
|
1030
|
+
`;
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
### `Icon`
|
|
1034
|
+
|
|
1035
|
+
The Icon component provides a flexible way to display icons in your application. It supports both Material Symbols (font-based icons) and SVG icons (Material Icons or custom SVG components), with consistent sizing and color control.
|
|
1036
|
+
|
|
1037
|
+
**When to use**
|
|
1038
|
+
|
|
1039
|
+
- Display icons alongside text or buttons
|
|
1040
|
+
- Represent actions, states, or categories visually
|
|
1041
|
+
- Provide visual cues in UI elements
|
|
1042
|
+
|
|
1043
|
+
**Peer dependencies:**
|
|
1044
|
+
|
|
1045
|
+
- `@emotion/react`
|
|
1046
|
+
- `react`
|
|
1047
|
+
- `react-dom`
|
|
1048
|
+
- `typescript`
|
|
1049
|
+
|
|
1050
|
+
See the `peerDependencies` section of `package.json` for compatible versions.
|
|
1051
|
+
|
|
1052
|
+
See [icon custom component build](#icon-custom-component-build) for usage without React/Emotion.
|
|
1053
|
+
|
|
1054
|
+
#### Example usage
|
|
1055
|
+
|
|
1056
|
+
See "Emotion/React" heading under the `Icon` component on [codesandbox.io](https://codesandbox.io/p/sandbox/mrzkrw).
|
|
1057
|
+
|
|
1058
|
+
```tsx
|
|
1059
|
+
import { Icon } from '@guardian/stand/icon';
|
|
1060
|
+
import { baseColors } from '@guardian/stand';
|
|
1061
|
+
|
|
1062
|
+
/* types, if required */
|
|
1063
|
+
import type { IconProps, IconTheme } from '@guardian/stand/icon';
|
|
1064
|
+
|
|
1065
|
+
/* Material Symbols (font icon) */
|
|
1066
|
+
<Icon size="md" symbol="home"></Icon>
|
|
1067
|
+
|
|
1068
|
+
/* Material Symbols with custom color */
|
|
1069
|
+
<Icon size="md" fill={baseColors.red[500]} symbol="home"></Icon>
|
|
1070
|
+
|
|
1071
|
+
/* Standalone meaningful icon (use alt prop) */
|
|
1072
|
+
<Icon alt="Warning: High priority" symbol="warning"></Icon>
|
|
1073
|
+
|
|
1074
|
+
/* Material Icons (SVG) */
|
|
1075
|
+
import HomeIcon from '@material-design-icons/svg/outlined/home.svg?react';
|
|
1076
|
+
<Icon size="md" alt="Home">
|
|
1077
|
+
<HomeIcon />
|
|
1078
|
+
</Icon>
|
|
1079
|
+
|
|
1080
|
+
/* Custom SVG component */
|
|
1081
|
+
const CustomIcon = () => (
|
|
1082
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
1083
|
+
<path d="M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z" />
|
|
1084
|
+
</svg>
|
|
1085
|
+
);
|
|
1086
|
+
|
|
1087
|
+
<Icon size="lg" fill={baseColors.blue[500]} alt="Shield protection">
|
|
1088
|
+
<CustomIcon />
|
|
1089
|
+
</Icon>
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
#### Props
|
|
1093
|
+
|
|
1094
|
+
| Name | Type | Required | Default | Description |
|
|
1095
|
+
| -------------- | ------------------------------- | ----------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1096
|
+
| `symbol` | `MaterialSymbol` | Conditional | N/A | Name of the Material Symbol to display (e.g., "home", "add"). Uses the `MaterialSymbol` type for type safety. Alternative to providing the icon as a child. |
|
|
1097
|
+
| `children` | `ReactNode` \| `MaterialSymbol` | Conditional | N/A | Icon content - either a Material Symbol name (typed as `MaterialSymbol`), an SVG component (ReactNode), or left empty if using `symbol`. |
|
|
1098
|
+
| `size` | `'sm'` \| `'md'` \| `'lg'` | No | `'md'` | Controls the icon dimensions. |
|
|
1099
|
+
| `fill` | `string` | No | N/A | Fill/color of the icon. Default is to inherit from text color or icon defaults. |
|
|
1100
|
+
| `alt` | `string` | Conditional | N/A | Alternative text for screen readers. Required if the icon conveys meaning on its own. Omit for decorative icons alongside text. |
|
|
1101
|
+
| `theme` | `IconTheme` (partial) | No | N/A | Override Stand tokens for this instance; merged over the default theme. |
|
|
1102
|
+
| `cssOverrides` | `SerializedStyles` | No | N/A | Low-level escape hatch for Emotion overrides when theming is insufficient. |
|
|
1103
|
+
| `className` | `string` | No | N/A | Additional class name(s) for the icon. For Material Symbols, combines with `material-symbols`. |
|
|
1104
|
+
|
|
1105
|
+
**`size`**
|
|
1106
|
+
|
|
1107
|
+
The icon supports three sizes:
|
|
1108
|
+
|
|
1109
|
+
- `sm` (small): 20px
|
|
1110
|
+
- `md` (medium): 24px
|
|
1111
|
+
- `lg` (large): 32px
|
|
1112
|
+
|
|
1113
|
+
**`symbol`**
|
|
1114
|
+
|
|
1115
|
+
The `symbol` prop provides a convenient way to specify a Material Symbol icon by name (typed as `MaterialSymbol`, e.g., `"home"`, `"add"`, `"close"`). This is the recommended approach for font-based icons. If both `symbol` and `children` are provided, `symbol` takes precedence for Material Symbols.
|
|
1116
|
+
|
|
1117
|
+
**`children`**
|
|
1118
|
+
|
|
1119
|
+
The Icon component accepts two types of children:
|
|
1120
|
+
|
|
1121
|
+
- **MaterialSymbol**: Pass the icon name as a string typed as `MaterialSymbol` (e.g., `"home"`, `"add"`, `"close"`). Requires the Material Symbols font to be loaded. (Prefer using the `symbol` prop for clarity.)
|
|
1122
|
+
- **ReactNode** (SVG): Pass an SVG component (e.g., from `@material-design-icons/svg` or a custom SVG component).
|
|
1123
|
+
|
|
1124
|
+
**`alt`**
|
|
1125
|
+
|
|
1126
|
+
Provide alternative text to describe the icon's meaning for screen readers:
|
|
1127
|
+
|
|
1128
|
+
- **When to use**: Icons that convey meaning on their own (status indicators, standalone alerts, informational graphics)
|
|
1129
|
+
- **When to omit**: Icons that are purely decorative or appear alongside descriptive text
|
|
1130
|
+
|
|
1131
|
+
#### Customisation
|
|
1132
|
+
|
|
1133
|
+
We recommend using the default theme. When needed, use the `theme` or `cssOverrides` props.
|
|
1134
|
+
|
|
1135
|
+
**Custom theme**
|
|
1136
|
+
|
|
1137
|
+
```tsx
|
|
1138
|
+
import type { IconTheme } from '@guardian/stand/icon';
|
|
1139
|
+
import { Icon } from '@guardian/stand/icon';
|
|
1140
|
+
import { baseSizing } from '@guardian/stand';
|
|
1141
|
+
|
|
1142
|
+
const customTheme: Partial<IconTheme> = {
|
|
1143
|
+
shared: {
|
|
1144
|
+
display: 'block',
|
|
1145
|
+
'user-select': 'all',
|
|
1146
|
+
},
|
|
1147
|
+
md: {
|
|
1148
|
+
size: baseSizing['size-48-rem'],
|
|
1149
|
+
},
|
|
1150
|
+
};
|
|
1151
|
+
|
|
1152
|
+
const Component = () => (
|
|
1153
|
+
<Icon size="md" theme={customTheme}>
|
|
1154
|
+
home
|
|
1155
|
+
</Icon>
|
|
1156
|
+
);
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
**CSS overrides**
|
|
1160
|
+
|
|
1161
|
+
```tsx
|
|
1162
|
+
import { Icon } from '@guardian/stand/icon';
|
|
1163
|
+
import { baseColors, baseSpacing, semanticSizing } from '@guardian/stand';
|
|
1164
|
+
import { css } from '@emotion/react';
|
|
1165
|
+
|
|
1166
|
+
const customStyles = css`
|
|
1167
|
+
padding: ${baseSpacing['4-rem']};
|
|
1168
|
+
background-color: ${baseColors.yellow[400]};
|
|
1169
|
+
border-radius: ${semanticSizing.border['extra-wide']};
|
|
1170
|
+
`;
|
|
1171
|
+
|
|
1172
|
+
const Component = () => (
|
|
1173
|
+
<Icon size="lg" fill={baseColors.neutral[900]} cssOverrides={customStyles}>
|
|
1174
|
+
home
|
|
1175
|
+
</Icon>
|
|
1176
|
+
);
|
|
1177
|
+
```
|
|
1178
|
+
|
|
1179
|
+
#### Icon Custom Component Build
|
|
1180
|
+
|
|
1181
|
+
If you're not using React/Emotion, or `@guardian/stand` is not compatible with your project, you can create a custom Icon component using the styles defined in the `IconTheme` type.
|
|
1182
|
+
|
|
1183
|
+
You will however be responsible for any additional functionality on top of the styles, for example icon loading, accessibility, etc.
|
|
1184
|
+
|
|
1185
|
+
See "Custom Component" heading under the `Icon` component on [codesandbox.io](https://codesandbox.io/p/sandbox/mrzkrw) for an example of a custom component build without React/Emotion.
|
|
1186
|
+
|
|
1187
|
+
**`css`**
|
|
1188
|
+
|
|
1189
|
+
You can import the Icon styles as CSS from the package, make sure that your build process supports importing CSS from `node_modules`/`package.json`:
|
|
1190
|
+
|
|
1191
|
+
```css
|
|
1192
|
+
/* import the icon font and icon styles */
|
|
1193
|
+
@import '@guardian/stand/fonts/MaterialSymbolsOutlined.css';
|
|
1194
|
+
@import '@guardian/stand/component/icon.css';
|
|
1195
|
+
|
|
1196
|
+
/*
|
|
1197
|
+
or for scenarios where you have to use relative paths/node_modules directly:
|
|
1198
|
+
|
|
1199
|
+
@import 'node_modules/@guardian/stand/dist/fonts/MaterialSymbolsOutlined.css';
|
|
1200
|
+
@import 'node_modules/@guardian/stand/dist/style/build/css/component/icon.css';
|
|
1201
|
+
*/
|
|
1202
|
+
|
|
1203
|
+
@import '@guardian/stand/base/spacing.css';
|
|
1204
|
+
@import '@guardian/stand/base/colors.css';
|
|
1205
|
+
|
|
1206
|
+
.stand-icon {
|
|
1207
|
+
display: var(--component-icon-shared-display);
|
|
1208
|
+
user-select: var(--component-icon-shared-user-select);
|
|
1209
|
+
font-size: var(--component-icon-lg-size);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.stand-icon-font-color {
|
|
1213
|
+
color: var(--base-colors-magenta-400);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
.stand-icon-svg > svg {
|
|
1217
|
+
width: var(--component-icon-lg-size);
|
|
1218
|
+
height: var(--component-icon-lg-size);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
.stand-icon-svg-color > svg {
|
|
1222
|
+
fill: var(--base-colors-magenta-400);
|
|
1223
|
+
}
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
```html
|
|
1227
|
+
<!-- Material Symbols (font icons) -->
|
|
1228
|
+
<span class="material-symbols stand-icon">home</span>
|
|
1229
|
+
<span class="material-symbols stand-icon stand-icon-font-color">upload</span>
|
|
1230
|
+
|
|
1231
|
+
<!-- SVG icons -->
|
|
1232
|
+
<span class="material-symbols stand-icon stand-icon-svg">
|
|
1233
|
+
<svg xmlns="http://www.w3.org/2000/svg" ...>...</svg>
|
|
1234
|
+
</span>
|
|
1235
|
+
<span class="material-symbols stand-icon stand-icon-svg stand-icon-svg-color">
|
|
1236
|
+
<svg xmlns="http://www.w3.org/2000/svg" ...>...</svg>
|
|
1237
|
+
</span>
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
**TypeScript/JavaScript**
|
|
1241
|
+
|
|
1242
|
+
Use the `componentIcon` variable and the `ComponentIcon` type to define your custom styles in TypeScript/JavaScript:
|
|
1243
|
+
|
|
1244
|
+
```ts
|
|
1245
|
+
import type { ComponentIcon } from '@guardian/stand'; // if types required
|
|
1246
|
+
import { componentIcon, baseColors } from '@guardian/stand';
|
|
1247
|
+
|
|
1248
|
+
const iconStyles = `
|
|
1249
|
+
display: ${componentIcon.shared.display};
|
|
1250
|
+
user-select: ${componentIcon.shared['user-select']};
|
|
1251
|
+
font-size: ${componentIcon.lg.size};
|
|
1252
|
+
`;
|
|
1253
|
+
|
|
1254
|
+
const iconFontColorStyles = `
|
|
1255
|
+
${iconStyles}
|
|
1256
|
+
color: ${baseColors.magenta[400]};
|
|
1257
|
+
`;
|
|
1258
|
+
|
|
1259
|
+
const iconSvgStyles = `
|
|
1260
|
+
width: ${componentIcon.lg.size};
|
|
1261
|
+
height: ${componentIcon.lg.size};
|
|
1262
|
+
`;
|
|
1263
|
+
|
|
1264
|
+
const iconSvgColorStyles = `
|
|
1265
|
+
${iconSvgStyles}
|
|
1266
|
+
fill: ${baseColors.magenta[400]};
|
|
1267
|
+
`;
|
|
1268
|
+
|
|
1269
|
+
// e.g. adding to DOM using vanilla JS styles
|
|
1270
|
+
document.getElementById('app')!.innerHTML = `
|
|
1271
|
+
<h3>
|
|
1272
|
+
Using <code>typescript</code>/<code>javascript</code>
|
|
1273
|
+
</h3>
|
|
1274
|
+
<div>see <code>src/icon/custom.ts</code><div>
|
|
1275
|
+
<div style="margin-top: 4px;">Material Symbols</div>
|
|
1276
|
+
<div class="container">
|
|
1277
|
+
<span class="material-symbols" style="${iconStyles}">home</span>
|
|
1278
|
+
<span class="material-symbols" style="${iconFontColorStyles}">upload</span>
|
|
1279
|
+
</div>
|
|
1280
|
+
<div style="margin-top: 4px;">SVGs</div>
|
|
1281
|
+
<div class="container">
|
|
1282
|
+
<span class="material-symbols" style="${iconStyles}"><svg style="${iconSvgStyles}"...>...</svg></span>
|
|
1283
|
+
<span class="material-symbols" style="${iconFontColorStyles}"><svg style="${iconSvgColorStyles}"...>...</svg></span>
|
|
1284
|
+
</div>
|
|
1285
|
+
`;
|
|
1286
|
+
```
|
|
1287
|
+
|
|
1288
|
+
### `LinkButton`
|
|
1289
|
+
|
|
1290
|
+
An anchor element styled like the Stand `Button` component, based on the React Aria `Link`. It keeps link semantics (`href`, `target`, `rel`) while sharing the same variants and sizes as `Button`.
|
|
1291
|
+
|
|
1292
|
+
**When to use**
|
|
1293
|
+
|
|
1294
|
+
- Navigational links that should visually align with buttons
|
|
1295
|
+
- Cross-page CTAs where a button look-and-feel is preferred
|
|
1296
|
+
- Situations requiring disabled styling while preserving link semantics
|
|
1297
|
+
|
|
1298
|
+
**Peer dependencies:**
|
|
1299
|
+
|
|
1300
|
+
- `@emotion/react`
|
|
1301
|
+
- `react`
|
|
1302
|
+
- `react-dom`
|
|
1303
|
+
- `react-aria-components`
|
|
1304
|
+
- `typescript`
|
|
1305
|
+
|
|
1306
|
+
See the `peerDependencies` section of `package.json` for compatible versions.
|
|
1307
|
+
|
|
1308
|
+
See [link button custom component build](#button-custom-component-build) for usage without React/Emotion.
|
|
1309
|
+
|
|
1310
|
+
#### Example usage
|
|
1311
|
+
|
|
1312
|
+
See "Emotion/React" heading on [codesandbox.io](https://codesandbox.io/p/sandbox/jctg2k)
|
|
1313
|
+
|
|
1314
|
+
```tsx
|
|
1315
|
+
import { LinkButton } from '@guardian/stand/button';
|
|
1316
|
+
import type { LinkButtonTheme, LinkButtonProps } from '@guardian/stand/button';
|
|
1317
|
+
|
|
1318
|
+
const Example = () => (
|
|
1319
|
+
<>
|
|
1320
|
+
<LinkButton
|
|
1321
|
+
href="/article"
|
|
1322
|
+
variant="emphasised-primary"
|
|
1323
|
+
size="md"
|
|
1324
|
+
target="_blank"
|
|
1325
|
+
rel="noreferrer"
|
|
1326
|
+
>
|
|
1327
|
+
Read article
|
|
1328
|
+
</LinkButton>
|
|
1329
|
+
|
|
1330
|
+
<LinkButton
|
|
1331
|
+
href="/settings"
|
|
1332
|
+
variant="neutral-secondary"
|
|
1333
|
+
size="sm"
|
|
1334
|
+
isDisabled
|
|
1335
|
+
>
|
|
1336
|
+
Disabled link
|
|
1337
|
+
</LinkButton>
|
|
1338
|
+
</>
|
|
1339
|
+
);
|
|
1340
|
+
```
|
|
1341
|
+
|
|
1342
|
+
#### Props
|
|
1343
|
+
|
|
1344
|
+
| Name | Type | Required | Default | Description |
|
|
1345
|
+
| -------------- | -------------------------------------------------------------------------------------------------- | -------- | ---------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
1346
|
+
| `href` | `string` | Yes | N/A | Destination URL for the link. |
|
|
1347
|
+
| `size` | `'xs'` \| `'sm'` \| `'md'` \| `'lg'` | No | `'md'` | Controls the link-button dimensions and typography. |
|
|
1348
|
+
| `variant` | `'emphasised-primary'` \| `'emphasised-secondary'` \| `'neutral-primary'` \| `'neutral-secondary'` | No | `'emphasised-primary'` | Chooses the colour scheme and interaction states. |
|
|
1349
|
+
| `children` | `ReactNode` | Yes | N/A | Content of the link. |
|
|
1350
|
+
| `isDisabled` | `boolean` | No | `false` | Disables interaction and applies disabled styling. |
|
|
1351
|
+
| `theme` | `LinkButtonTheme` (partial) | No | N/A | Override Stand tokens for this instance; merged over the default theme. |
|
|
1352
|
+
| `cssOverrides` | `SerializedStyles` \| `SerializedStyles[]` | No | N/A | Low-level escape hatch for Emotion overrides when theming is insufficient. |
|
|
1353
|
+
| `className` | `string` | No | N/A | Optional class name forwarded to the root React Aria link. |
|
|
1354
|
+
| `...props` | `ReactAria Link` props | No | N/A | All other props from `react-aria-components` `Link` (e.g. `target`, `rel`, `aria-label`, event handlers). |
|
|
1355
|
+
|
|
1356
|
+
**`size` and `variant`**
|
|
1357
|
+
|
|
1358
|
+
- Sizes: `xs`, `sm`, `md`, `lg`
|
|
1359
|
+
- Variants: `emphasised-primary`, `emphasised-secondary`, `neutral-primary`, `neutral-secondary`
|
|
1360
|
+
|
|
1361
|
+
#### Customisation
|
|
1362
|
+
|
|
1363
|
+
We recommend using the default theme. When needed, use the `theme` or `cssOverrides` props.
|
|
1364
|
+
|
|
1365
|
+
**Custom theme**
|
|
1366
|
+
|
|
1367
|
+
```tsx
|
|
1368
|
+
import type { LinkButtonTheme } from '@guardian/stand/button';
|
|
1369
|
+
import { LinkButton } from '@guardian/stand/button';
|
|
1370
|
+
import { baseColors } from '@guardian/stand';
|
|
1371
|
+
|
|
1372
|
+
const customTheme: Partial<LinkButtonTheme> = {
|
|
1373
|
+
'emphasised-primary': {
|
|
1374
|
+
shared: {
|
|
1375
|
+
backgroundColor: baseColors['cool-purple'][200],
|
|
1376
|
+
color: baseColors['cool-purple'][900],
|
|
1377
|
+
border: `2px solid ${baseColors['cool-purple'][700]}`,
|
|
1378
|
+
':hover': {
|
|
1379
|
+
backgroundColor: baseColors['cool-purple'][300],
|
|
1380
|
+
border: `2px solid ${baseColors['cool-purple'][700]}`,
|
|
1381
|
+
},
|
|
1382
|
+
':active': {
|
|
1383
|
+
backgroundColor: baseColors['cool-purple'][400],
|
|
1384
|
+
border: `2px solid ${baseColors['cool-purple'][700]}`,
|
|
1385
|
+
},
|
|
1386
|
+
},
|
|
1387
|
+
},
|
|
1388
|
+
};
|
|
1389
|
+
|
|
1390
|
+
const Component = () => (
|
|
1391
|
+
<LinkButton
|
|
1392
|
+
href="/"
|
|
1393
|
+
variant="emphasised-primary"
|
|
1394
|
+
size="md"
|
|
1395
|
+
theme={customTheme}
|
|
1396
|
+
>
|
|
1397
|
+
Custom Themed LinkButton
|
|
1398
|
+
</LinkButton>
|
|
1399
|
+
);
|
|
1400
|
+
```
|
|
1401
|
+
|
|
1402
|
+
**CSS overrides**
|
|
1403
|
+
|
|
1404
|
+
```tsx
|
|
1405
|
+
import { LinkButton } from '@guardian/stand/button';
|
|
1406
|
+
import { baseSpacing } from '@guardian/stand';
|
|
1407
|
+
import { css } from '@emotion/react';
|
|
1408
|
+
|
|
1409
|
+
const customStyles = css`
|
|
1410
|
+
width: 100%;
|
|
1411
|
+
text-transform: full-width;
|
|
1412
|
+
font-variant: small-caps;
|
|
1413
|
+
padding-inline: ${baseSpacing['24-rem']};
|
|
1414
|
+
`;
|
|
1415
|
+
|
|
1416
|
+
const Component = () => (
|
|
1417
|
+
<LinkButton
|
|
1418
|
+
href="/"
|
|
1419
|
+
variant="neutral-primary"
|
|
1420
|
+
size="sm"
|
|
1421
|
+
cssOverrides={customStyles}
|
|
1422
|
+
>
|
|
1423
|
+
CSSOverrides LinkButton
|
|
1424
|
+
</LinkButton>
|
|
1425
|
+
);
|
|
1426
|
+
```
|
|
1427
|
+
|
|
1428
|
+
#### LinkButton Custom Component Build
|
|
1429
|
+
|
|
1430
|
+
LinkButton shares the same tokens and CSS output as `Button`. Use the [Button custom component build](#button-custom-component-build) guidance to consume the CSS or TypeScript tokens when you need a non-React implementation.
|
|
1431
|
+
|
|
1432
|
+
### Typography
|
|
1433
|
+
|
|
1434
|
+
The Typography component provides a convenient way to wrap React elements in a font variant.
|
|
1435
|
+
|
|
1436
|
+
**Peer dependencies**
|
|
1437
|
+
|
|
1438
|
+
- `@emotion/react`
|
|
1439
|
+
- `react`
|
|
1440
|
+
- `react-dom`
|
|
1441
|
+
- `typescript`
|
|
1442
|
+
|
|
1443
|
+
See the `peerDependencies` section of `package.json` for compatible versions.
|
|
1444
|
+
|
|
1445
|
+
#### Example usage
|
|
1446
|
+
|
|
1447
|
+
```tsx
|
|
1448
|
+
import { Typography } from '@guardian/stand/typography';
|
|
1449
|
+
|
|
1450
|
+
/* types, if required */
|
|
1451
|
+
import type { Typography, TypographyTheme } from '@guardian/stand/typography';
|
|
1452
|
+
|
|
1453
|
+
/* Paragraph element with body-md preset and text children */
|
|
1454
|
+
<Typography element="p" variant="body-md">Body text here</Typography>
|
|
1455
|
+
|
|
1456
|
+
/* Div with an italic text wrapped inside */
|
|
1457
|
+
<Typography element="div" variant="body-md">Some text, with <Typography element="i" variant="body-italic-md">even more text</Typography></Typography>
|
|
1458
|
+
```
|
|
1459
|
+
|
|
1460
|
+
#### Props
|
|
1461
|
+
|
|
1462
|
+
| Name | Type | Required | Default | Description |
|
|
1463
|
+
| -------------- | ------------------ | -------- | --------- | ---------------------------------------------------- |
|
|
1464
|
+
| `element` | Various | No | 'span' | HTML element to render with font applied to. |
|
|
1465
|
+
| `variant` | Various | No | 'body-md' | Font variant to apply as a CSS style to the element. |
|
|
1466
|
+
| `children` | `ReactNode` | No | N/A | Content to render inside the supplied HTML element. |
|
|
1467
|
+
| `theme` | `TypographyTheme` | No | N/A | Custom theme overrides for the typography. |
|
|
1468
|
+
| `cssOverrides` | `SerializedStyles` | No | N/A | Custom CSS styles for the typography. |
|
|
1469
|
+
| `className` | `string` | No | N/A | Additional class name(s) for the typography. |
|
|
1470
|
+
|
|
1471
|
+
#### Customisation
|
|
1472
|
+
|
|
1473
|
+
**Custom theme**
|
|
1474
|
+
|
|
1475
|
+
The `theme` prop allows you to override the color of the text:
|
|
1476
|
+
|
|
1477
|
+
```tsx
|
|
1478
|
+
import type { TypographyTheme } from '@guardian/stand/typography';
|
|
1479
|
+
import { Typography } from '@guardian/stand/typography';
|
|
1480
|
+
|
|
1481
|
+
const customTheme: TypographyTheme = {
|
|
1482
|
+
color: 'red',
|
|
1483
|
+
};
|
|
1484
|
+
const Component = () => (
|
|
1485
|
+
<Typography element="p" variant="body-md" theme={customTheme}>
|
|
1486
|
+
Text
|
|
1487
|
+
</Typography>
|
|
1488
|
+
);
|
|
1489
|
+
```
|
|
1490
|
+
|
|
1491
|
+
**CSS overrides**
|
|
1492
|
+
|
|
1493
|
+
The `cssOverrides` prop allows you to pass custom CSS to the rendered element.
|
|
1494
|
+
|
|
1495
|
+
## Components - Editorial
|
|
1496
|
+
|
|
1497
|
+
Specialised components for use in specific editorial use cases.
|
|
457
1498
|
|
|
458
1499
|
### `Byline`
|
|
459
1500
|
|
|
@@ -627,6 +1668,86 @@ const Component = () => {
|
|
|
627
1668
|
|
|
628
1669
|
This is currently still in testing phase, so a production implementation is not yet available.
|
|
629
1670
|
|
|
1671
|
+
### `UserMenu`
|
|
1672
|
+
|
|
1673
|
+
The UserMenu component presents a collection of accessibility settings for users to customise their experience of using the application. The current supported settings are:
|
|
1674
|
+
|
|
1675
|
+
- "Text Size"
|
|
1676
|
+
- "Font Family"
|
|
1677
|
+
- "Color scheme"
|
|
1678
|
+
|
|
1679
|
+
**Peer dependencies:**
|
|
1680
|
+
|
|
1681
|
+
- `@emotion/react`
|
|
1682
|
+
- `react`
|
|
1683
|
+
- `react-dom`
|
|
1684
|
+
- `typescript`
|
|
1685
|
+
- `react-aria-components`
|
|
1686
|
+
|
|
1687
|
+
See the `peerDependencies` section of `package.json` for compatible versions.
|
|
1688
|
+
|
|
1689
|
+
**When to use**
|
|
1690
|
+
|
|
1691
|
+
- as an application-level "accessibility options" panel
|
|
1692
|
+
- as part of a more general options page
|
|
1693
|
+
|
|
1694
|
+
#### Usage
|
|
1695
|
+
|
|
1696
|
+
```tsx
|
|
1697
|
+
import { UserMenu, type UserMenuProps } from '@guardian/stand/user-menu';
|
|
1698
|
+
|
|
1699
|
+
const customFontFamilyOptions = [
|
|
1700
|
+
{
|
|
1701
|
+
id: 'white',
|
|
1702
|
+
buttonStyle: {
|
|
1703
|
+
backgroundColor: 'white',
|
|
1704
|
+
},
|
|
1705
|
+
isDefault: true,
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
id: 'pink',
|
|
1709
|
+
buttonStyle: {
|
|
1710
|
+
backgroundColor: 'pink',
|
|
1711
|
+
},
|
|
1712
|
+
},
|
|
1713
|
+
];
|
|
1714
|
+
|
|
1715
|
+
const Component = ({
|
|
1716
|
+
currentPreferences,
|
|
1717
|
+
updatePreferences,
|
|
1718
|
+
}: {
|
|
1719
|
+
currentPreferences: UserMenuProps['preferences'];
|
|
1720
|
+
updatePreferences: UserMenuProps['updatePreferences'];
|
|
1721
|
+
}) => {
|
|
1722
|
+
...
|
|
1723
|
+
return (
|
|
1724
|
+
<>
|
|
1725
|
+
...
|
|
1726
|
+
<UserMenu
|
|
1727
|
+
feedBacklink="https://example.com/feedback-form"
|
|
1728
|
+
fontFamilyOptions={customFontFamilyOptions}
|
|
1729
|
+
colorSchemeOptions={[]}
|
|
1730
|
+
preferences={currentPreferences}
|
|
1731
|
+
updatePreferences={updatePreferences}
|
|
1732
|
+
/>
|
|
1733
|
+
...
|
|
1734
|
+
</>
|
|
1735
|
+
);
|
|
1736
|
+
};
|
|
1737
|
+
```
|
|
1738
|
+
|
|
1739
|
+
`UserMenu` does not manage the persistence of the settings, nor how they are applied in the application when set - it merely presents a set of [controlled](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components) inputs for displaying and setting the values held by the application.
|
|
1740
|
+
|
|
1741
|
+
There are options defaults for each of the setting, but these can be overridden with the props. IF the application does not support customising one of the options, setting an empty array for one of the sets of options will exclude that option from the UI (like in `colorSchemeOptions` in the example above).
|
|
1742
|
+
|
|
1743
|
+
#### Props
|
|
1744
|
+
|
|
1745
|
+
See [`UserMenuProps`](src/user-menu/UserMenu.tsx#L14) for the full list of props, usage example can be seen in Storybook.
|
|
1746
|
+
|
|
1747
|
+
#### Example
|
|
1748
|
+
|
|
1749
|
+
This is currently still in testing phase, so a production implementation is not yet available.
|
|
1750
|
+
|
|
630
1751
|
### Contributing
|
|
631
1752
|
|
|
632
1753
|
See the [Contributing to Stand](./CONTRIBUTING.md) documentation for guidelines on contributing to this project. Project setup and common tasks are listed below.
|