Skip to content
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

docs: add more documentation on how to do theme overrides #1800

Merged
merged 1 commit into from
Dec 3, 2024
Merged
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
295 changes: 230 additions & 65 deletions docs/guides/using-theme-overrides.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ order: 2
This document gives an overview on how you can customize Instructure UI components by tweaking their theme variables.
While this gives you a level of flexibility on the look and feel of the components you should be aware of 2 things:

- The default theme variables are tested to have high enough contrast ratios for WCAG 2.1 conformance. If you are making changes, please make sure that your app stays WCAG conformant.
- The default theme variables are tested to have high enough contrast ratios for WCAG 2.1 AA conformance. If you are making changes, please make sure that your app stays WCAG conformant.
- The look of components is only customisable to a certain degree. This is intentional, because Instructure UI is a design system geared towards the Canvas "look and feel", not a generic UI component library.

```js
Expand All @@ -25,63 +25,32 @@ type: embed
</ToggleBlockquote>
```

### How theming works in InstUI
### Using a different theme for a DOM subtree

The theming system in InstUI has these levels:

**The application level theme**

On the broader level, there is the main theme object that contains the color, spacing, typography etc. variables available in the theme (e.g.: [canvas theme](/#canvas)). The application level theme can be set via the [InstUISettingsProvider](/#InstUISettingsProvider) component.
By nesting the `InstUISettingsProvider` you can apply different themes to some sections of you app.

```jsx
```js
---
type: code
type: example
---
// app/component root sets the app theme
<InstUISettingsProvider theme={canvas}>
<ExampleComponent />
</InstUISettingsProvider>
```

**The component's own theme**

Every themeable component has its own "theme map". This map defines the components own theme variables (used by this component only), and maps them to values in the global theme object. These local variables are then passed to the component and used in the styles object.

See the [emotion](/#emotion), [built-in themes](/#ui-themes) and [InstUISettingsProvider](/#InstUISettingsProvider) docs pages for more info and examples.

Either you set up the themes globally, or you use the `InstUISettingsProvider` to set up themes, the component's `theme.js` will map it to theme variables:

```jsx
---
type: code
---
// component's `theme.js` maps the
const generateComponentTheme = (theme) => {
const { colors } = theme // global theme, e.g.: canvas theme

return {
background: colors?.UI?.surfaceDark,
color: colors?.UI?.textSuccess
//...
}
}
<div>
<Alert variant="info" margin="small">
I am a "canvas" style Alert.
</Alert>

// component's `style.js` uses the theme variables
const generateStyle = (componentTheme) => {
return {
button: {
label: 'button',
background: componentTheme.background,
color: componentTheme.color
//...
}
}
}
<InstUISettingsProvider theme={canvasHighContrast}>
<Alert variant="info" margin="small">
I am a "canvasHighContrast" style Alert.
</Alert>
</InstUISettingsProvider>
</div>
</InstUISettingsProvider>
```

### Application level theme overrides
### Overriding parts of a theme

`<InstUISettingsProvider/>` accepts full theme objects and override objects too.
You can modify parts of a theme object:

```js
---
Expand All @@ -101,9 +70,45 @@ type: example
</InstUISettingsProvider>
```

#### Full themes
Or modify a theme inside a subtree in 2 ways:

By nesting the `InstUISettingsProvider` you can apply different themes to some sections of you app.
```js
---
type: example
---
<InstUISettingsProvider theme={canvas}>
<div>
<Heading level="h3" margin="small small medium">
I should have default font family.
</Heading>

<InstUISettingsProvider
theme={{ typography: { fontFamily: 'monospace' } }}
>
<Heading level="h3" margin="small small">
monospace font family set via override on the parent theme.
</Heading>
</InstUISettingsProvider>
<InstUISettingsProvider
theme={{
themeOverrides: {
canvas: { typography: { fontFamily: 'monospace' } }
}
}}
>
<Heading level="h3" margin="small small">
monospace font family set via override only if the parent theme is 'canvas'.
</Heading>
</InstUISettingsProvider>
</div>
</InstUISettingsProvider>
```

### Overriding theme for a specific component in a subtree

You can override the theme variables of specific components too with the `componentOverrides` key. You can do this for every theme or for just a given theme.

**Important:** these variables are the components own theme variables set in the `theme.js` of the component.

```js
---
Expand All @@ -112,43 +117,203 @@ type: example
<InstUISettingsProvider theme={canvas}>
<div>
<Alert variant="info" margin="small">
I am a "canvas" style Alert.
I am a default style Alert.
</Alert>

<InstUISettingsProvider theme={canvasHighContrast}>
<InstUISettingsProvider
theme={{
componentOverrides: {
Alert: {
infoIconBackground: "darkblue",
infoBorderColor: "darkblue"
}
}
}}
>
<Alert variant="info" margin="small">
I am a "canvasHighContrast" style Alert.
My icon background and border should be dark blue in any theme.
</Alert>
</InstUISettingsProvider>

<InstUISettingsProvider
theme={{
themeOverrides: {
canvas: {
componentOverrides: {
Alert: {
warningIconBackground: "deeppink",
warningBorderColor: "deeppink"
}
}
}
}
}}
>
<Alert variant="warning" margin="small">
My border and icon background should be deep pink just if the 'canvas' theme is active.
</Alert>
</InstUISettingsProvider>
</div>
</InstUISettingsProvider>
```

#### Partial theme overrides
For child components both the displayName (`'InlineList.Item'`) and the componentId (`List.Item.componentId`) can be used as keys in `componentOverrides`.

When providing a partial theme object, you can override any theme variable inside that provider.
```jsx
---
type: code
---
<InstUISettingsProvider
theme={{
componentOverrides: {
'InlineList.Item': {
color: 'blue'
},
[List.Item.componentId]: {
color: 'red'
}
}
}}
>
{/* ... */}
</InstUISettingsProvider>
```

### Overriding theme for a single component

Themeable components (that implement the [withStyle](#withStyle) decorator) accept a `themeOverride` prop. This prop let's you override the component's own theme. It accepts an object or a function.

The available theme variables are always displayed at the bottom of the component's page (e.g.: [Button component theme variables](/#Button/#ButtonTheme)).

#### Override object

```js
---
type: example
---
<InstUISettingsProvider theme={canvas}>
<div>
<div>
<Heading level="h3" margin="small small medium">
I should have default font family.
</Heading>
<Button color='primary'>Default Primary Button</Button>
</div>

<div>
<Button color='primary'
themeOverride={{ primaryBackground: "purple" }}
margin="small 0 large">
Purple Primary Button
</Button>
<InstUISettingsProvider
theme={{
typography: { fontFamily: 'monospace' }
componentOverrides: {
TextInput: { background: "yellow" }
}
}}
>
<Heading level="h3" margin="small small">
I should have monospace font family.
</Heading>
<DateInput2
renderLabel="This is how to handle override in a nested component"
screenReaderLabels={{
calendarIcon: 'Calendar',
nextMonthButton: 'Next month',
prevMonthButton: 'Previous month'
}}
width="20rem"
invalidDateErrorMessage="Invalid date"
/>
</InstUISettingsProvider>
</div>
</InstUISettingsProvider>
</div>
```

#### Override function

The override function's first parameter is the component's own theme object, the second is the main theme object.

```js
---
type: example
---
<div>
<div>
<Button color='primary'>Default Primary Button</Button>
</div>

<div>
<Button
color='primary'
margin="small 0 0"
themeOverride={(componentTheme) => ({
primaryBackground: componentTheme.successBackground,
primaryBorderColor: componentTheme.successBorderColor
})}
>
Default Primary Button with Success colors
</Button>
</div>
</div>
```

You can access and use any global theme variable via the second parameter (e.g. the [canvas theme](/#canvas)). When changing themes, these variables will update too.

```js
---
type: example
---
<div>
<div>
<Button color='primary'>Default Primary Button</Button>
</div>

<div>
<Button
color='primary'
margin="small 0 0"
themeOverride={(_componentTheme, currentTheme) => ({
primaryBackground: currentTheme.colors.primitives.orange57,
primaryBorderColor: currentTheme.colors.primitives.green45,
borderWidth: currentTheme.borders.widthLarge,
borderStyle: 'dashed'
})}
>
Custom Primary Button
</Button>
</div>
</div>
```

**The component's own theme**

Every themeable component has its own "theme map". This map defines the components own theme variables (used by this component only), and maps them to values in the global theme object. These local variables are then passed to the component and used in the styles object.

See the [emotion](/#emotion), [built-in themes](/#ui-themes) and [InstUISettingsProvider](/#InstUISettingsProvider) docs pages for more info and examples.

Either you set up the themes globally, or you use the `InstUISettingsProvider` to set up themes, the component's `theme.js` will map it to theme variables:

```jsx
---
type: code
---
// component's `theme.js` maps the
const generateComponentTheme = (theme) => {
const { colors } = theme // global theme, e.g.: canvas theme

return {
background: colors?.UI?.surfaceDark,
color: colors?.UI?.textSuccess
//...
}
}

// component's `style.js` uses the theme variables
const generateStyle = (componentTheme) => {
return {
button: {
label: 'button',
background: componentTheme.background,
color: componentTheme.color
//...
}
}
}
```

### Branding (user customizable theming)
Expand Down
4 changes: 2 additions & 2 deletions packages/emotion/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ type: example
color='primary'
margin="0 0 0 small"
themeOverride={(_componentTheme, currentTheme) => ({
primaryBackground: currentTheme.colors.backgroundWarning,
primaryBorderColor: currentTheme.colors.backgroundLightest,
primaryBackground: currentTheme.colors.primitives.orange57,
primaryBorderColor: '#00AAA4',
borderWidth: currentTheme.borders.widthLarge,
borderStyle: 'dashed'
})}
Expand Down
Loading
Loading