Theme: Update theme style guide and docs with new theme info and variable picking guide (#36530)

* Theme: Update theme style guide and docs with new theme info and variable picking guide

* Minor spell fix
pull/36631/head
Torkel Ödegaard 4 years ago committed by GitHub
parent ca2223f705
commit d4a990215f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 165
      contribute/style-guides/themes.md

@ -4,11 +4,8 @@
**Themes are implemented in Typescript.** That's because our goal is to share variables between Grafana TypeScript and [Sass](https://sass-lang.com/) code. Theme definitions are located in the following files:
- [packages/grafana-ui/src/themes/dark.ts](../../packages/grafana-ui/src/themes/dark.ts)
- [packages/grafana-ui/src/themes/default.ts](../../packages/grafana-ui/src/themes/default.ts)
- [packages/grafana-ui/src/themes/light.ts](../../packages/grafana-ui/src/themes/light.ts)
The `default.ts` file holds common variables like typography and spacing definitions, while `[light|dark].ts` primarily specify colors used in themes.
- [packages/grafana-data/src/themes/createTheme.ts](../../packages/grafana-data/src/themes/createTheme.ts)
- [packages/grafana-data/src/themes/createColors.ts](../../packages/grafana-data/src/themes/createColors.ts)
## Usage
@ -18,23 +15,22 @@ This section provides usage guidelines.
Here's how to use Grafana themes in React components.
#### useStyles hook
#### useStyles2 hook
`useStyles` memoizes the function and provides access to the theme.
`useStyles2` memoizes the function and provides access to the theme.
```tsx
import React, { FC } from 'react';
import { GrafanaTheme } from '@grafana/data';
import { useStyles } from '@grafana/ui';
import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2 } from '@grafana/ui';
import { css } from '@emotion/css';
const getComponentStyles = (theme: GrafanaTheme) => css`
const getComponentStyles = (theme: GrafanaTheme2) => css`
padding: ${theme.spacing.md};
`;
const Foo: FC<FooProps> = () => {
const styles = useStyles(getComponentsStyles);
const styles = useStyles2(getComponentsStyles);
// Use styles with className
};
```
@ -43,15 +39,89 @@ const Foo: FC<FooProps> = () => {
```tsx
import React, { FC } from 'react';
import { useTheme } from '@grafana/ui';
import { useTheme2 } from '@grafana/ui';
const Foo: FC<FooProps> = () => {
const theme = useTheme();
const theme = useTheme2();
// Your component has access to the theme variables now
};
```
## Picking the right variable
### The rich color object and the state colors
The theme.colors object has 6 rich color objects for `primary`, `secondary`, `info`, `success`, `warning` and `error`. These all
have the same sub colors that have different use cases.
| Property | When to use |
| ------------ | ---------------------------------------------------------- |
| main | For backgrounds |
| shade | For hover highlight |
| text | For text color |
| border | For borders, currently always the same as text color |
| contrastText | Text color to use for text placed on top of the main color |
Example use cases:
- Want a `red` background? Use `theme.colors.error.main`
- Want `green` text? Use `theme.colors.success.text`
- Want text to be visible when placed inside a background that uses `theme.colors.error.main` then use `theme.colors.error.contrastText`.
### Text colors
| Property | When to use |
| ----------------------------- | ------------------------------------------------------------------------------ |
| theme.colors.text.primary | The default text color |
| theme.colors.text.secondary | Text color for things that should be a bit less prominent |
| theme.colors.text.disabled | Text color for disabled / faint things |
| theme.colors.text.link | Text link color |
| theme.colors.text.maxContrast | Maximum contrast (absolute white in dark theme, absolute black in white theme) |
### Background colors
| Property | When to use |
| --------------------------------- | ------------------------------------------------------------------------------------------------- |
| theme.colors.background.canvas | Think dashboard background. A background surface for panels and panes that use primary background |
| theme.colors.background.primary | The default content background for content panes and panels |
| theme.colors.background.secondary | For cards and other surfaces that need to stand out when placed on top of the primary background |
### Borders
| Property | When to use |
| -------------------------- | ------------------------------------------------------------ |
| theme.colors.border.weak | Primary border for panels and panes and other subtle borders |
| theme.colors.border.medium | For stronger borders like inputs |
| theme.colors.border.strong | For even stronger border like hover highighted border |
### Actions
| Property | When to use |
| ---------------------------- | ----------------------------------------------------- |
| theme.colors.action.hover | Background color for hover on card, menu or list item |
| theme.colors.action.focus | Background color for focused card, menu or list item |
| theme.colors.action.selected | Background color for selected card, menu or list item |
### Paddings and margins
| Example | Result |
| --------------------------- | ----------------- |
| theme.spacing(1) | 8px |
| theme.spacing(1, 2) | 8px 16px |
| theme.spacing(1, 2, 0.5, 4) | 8px 16px 4px 32px |
### Border radius
| Example | Result |
| --------------------------- | ------ |
| theme.shape.borderRadius(1) | 2px |
| theme.shape.borderRadius(2) | 4px |
### Typography
For font family, font sizes and line heights use the variables under `theme.typography`.
#### Using `ThemeContext` directly
```tsx
@ -67,39 +137,25 @@ With this method your component will be automatically wrapped in `ThemeContext.C
```ts
import { ThemeContext, Themeable } from '@grafana/ui';
interface FooProps extends Themeable {}
interface FooProps extends Themeable2 {}
const Foo: React.FunctionComponent<FooProps> = () => ...
export default withTheme(Foo);
export default withTheme2(Foo);
```
### Test components that use `ThemeContext`
When implementing snapshot tests for components that use the `withTheme` HOC, the snapshot will contain the entire theme object. Any change to the theme renders the snapshot outdated.
### Using theme in tests
To make your snapshot theme independent, use the `mockThemeContext` helper function:
If you need to pass a theme object to a function under test just import `createTheme` and call it without
any arguments.
```tsx
import { mockThemeContext } from '@grafana/ui';
import { MyComponent } from './MyComponent';
import { createTheme } from '@grafana/data';
describe('MyComponent', () => {
let restoreThemeContext;
beforeAll(() => {
// Create ThemeContext mock before any snapshot test is executed
restoreThemeContext = mockThemeContext({ type: GrafanaThemeType.Dark });
});
afterAll(() => {
// Make sure the theme is restored after snapshot tests are performed
restoreThemeContext();
});
it('renders correctly', () => {
const wrapper = mount(<MyComponent />);
expect(wrapper).toMatchSnapshot();
it('should work', () => {
result = functionThatNeedsTheme(createTheme());
expect(result).toBe(true);
});
});
```
@ -116,36 +172,5 @@ This section provides insight into frequently-asked questions.
`[_variables|_variables.dark|_variables.light].generated.scss` files are the ones that are referenced in the main Sass files for Sass variables to be available. **These files are automatically generated and should never be modified by hand!**
#### If you need to modify a _Sass variable value_ you need to modify the corresponding Typescript file that is the source of the variables:
- `_variables.generated.scss` - modify `grafana-ui/src/themes/default.ts`
- `_variables.light.generated.scss` - modify `grafana-ui/src/themes/light.ts`
- `_variables.dark.generated.scss` - modify `grafana-ui/src/themes/dark.ts`
#### If you need to _add new variable_ to Sass variables you need to modify corresponding template file:
- `_variables.generated.scss` - modify `grafana-ui/src/themes/_variables.scss.tmpl.ts`
- `_variables.light.generated.scss` - modify `grafana-ui/src/themes/_variables.light.scss.tmpl.ts`
- `_variables.dark.generated.scss` - modify `grafana-ui/src/themes/_variables.dark.scss.tmpl.ts`
## Limitations
This section describes limitations with Grafana's theming system.
### You must ensure `ThemeContext` provider is available in a React tree
By default all react2angular directives have `ThemeContext.Provider` ensured. But, there are cases where we create another React tree via `ReactDOM.render`. This happens in the case of graph legend rendering and the `ReactContainer` directive. In such cases theme consumption will fail. To make sure theme context is available in such cases, you need to wrap your rendered component with ThemeContext.Provider using the `provideTheme` function:
```ts
// graph.ts
import { provideTheme } from 'app/core/utils/ConfigProvider';
// Create component with ThemeContext.Provider first.
// Otherwise React will create new components every time it renders!
const LegendWithThemeProvider = provideTheme(Legend);
const legendReactElem = React.createElement(LegendWithThemeProvider, legendProps);
ReactDOM.render(legendReactElem, this.legendElem, () => this.renderPanel());
```
`provideTheme` makes current theme available via ThemeContext by checking if user has `lightTheme` set in her boot data.
If you need to modify the sass variable files be sure to update the files that end with `.tmpl.ts` and
not the `.generated.scss` files.

Loading…
Cancel
Save