-
-
Save danieljpgo/bf0b30eb5066560e3ddc8cf04b816dd8 to your computer and use it in GitHub Desktop.
A construção do tema é sempre uma parte importante para o desenvolvimento de qualquer projeto, com styled-components
é possível desenvolver um tema de forma dinâmica para acelerar o desenvolvimento e facilita se manter consistente durante a criação dos estilos.
Inicialmente, para se criar o arquivo de tema para a aplicação, recomendamos seguir a seguinte estrutura:
/src
└── /app
├── ...
├── ...
└── /styles
├── global.ts
├── theme.ts
└── /tokens
├── breakpoints.ts
├── typography.ts
├── spacing.ts
├── shapes.ts
├── layers.ts
└── colors.ts
Para múltiplos temas:
/src
└── /app
├── ...
├── ...
└── /styles
├── global.ts
├── /theme
| ├── light.ts
| └── dark.ts
└── /tokens
├── breakpoints.ts
├── typography.ts
├── spacing.ts
├── shapes.ts
├── layers.ts
└── colors.ts
A partir disso, dentro do arquivo de tema:
- Importe os design tokens.
- Crie e exporte a variavel
theme
. - Dentro do
theme
, defina os valores dos designs tokens a serem utilizados pelo tema como no exemplo abaixo.
import {
breakpoints,
typography,
spacing,
shapes,
layers,
colors,
} from './tokens';
export const theme = {
title: 'main', // nome para auxiliar no debugging caso possua multiplos temas por meio React Devtools
colors: {
primary: {
lighter: colors.blue['200'],
light: colors.blue['400'],
main: colors.blue['500'],
dark: colors.blue['600'],
darker: colors.blue['800'],
},
secundary: {
lighter: colors.purple['200'],
light: colors.purple['400'],
main: colors.purple['500'],
dark: colors.purple['600'],
darker: colors.purple['800'],
},
tertiary: {
lighter: colors.pink['200'],
light: colors.pink['400'],
main: colors.pink['500'],
dark: colors.pink['600'],
darker: colors.pink['800'],
},
success: {
light: colors.green['400'],
main: colors.green['500'],
dark: colors.green['600'],
},
attention: {
light: colors.yellow['400'],
main: colors.yellow['500'],
dark: colors.yellow['600'],
},
error: {
light: colors.red['400'],
main: colors.red['500'],
dark: colors.red['600'],
},
surface: {
primary: colors.blue['100'],
secundary: colors.purple['100'],
tertiary: colors.pink['100'],
main: colors.white,
constrast: colors.gray['100'],
},
text: {
primary: colors.gray['800'],
secundary: colors.gray['300'],
tertiary: colors.gray['200'],
main: colors.blue['100'],
constrast: colors.gray['100'],
},
},
breakpoints,
typography,
spacing,
shapes,
layers,
} as const;
Os design tokens são uma forma de padronizar a estilização das interface, utilizando valores e nomes comuns, a fim de acelerar o desenvolvimento e aumentar sua consistência.
Segue o exemplo de tokens que já estão sendo utilizado em projetos, lembrando que o importante é a nomenclatura e a organização, não os valores.
// spacings.ts
export const spacing = {
'2xs': '2px',
xs: '4px',
sm: '8px',
md: '16px',
lg: '32px',
xl: '56px',
'2xl': '64px',
} as const;
// shapes.ts
export const shapes = {
borderRadius: {
sm: '4px',
md: '8px',
lg: '16px',
full: '999999px'
},
borderWidth: {
sm: '1px',
md: '2px',
lg: '4px',
}
} as const;
// breakpoints.ts
export const breakpoints = {
'2xs': '360px',
xs: '480px',
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
} as const;
// typography.ts
export const typography = {
family: { // @TODO adicionar exemplo
primary: '',
secondary: '',
},
weight: {
sm: '400',
md: '500',
lg: '600',
xl: '700',
},
sizes: {
xs: '12px',
sm: '14px',
md: '16px',
lg: '24px',
xl: '32px',
'2xl': '48px',
},
lineHeight: { // @TODO adicionar exemplo
sm: '',
md: '',
lg: '',
xl: '',
},
} as const;
// effects.ts
export const effects = {
opacity: {
xs: '0.1',
sm: '0.3',
md: '0.5',
lg: '0.7',
full: '1',
},
boxShadow: { // @TODO adicionar exemplo
sm: '',
md: '',
lg: '',
}
} as const;
// layers.ts
export const layers = {
base: 10,
menu: 20,
overlay: 30,
modal: 40,
alwaysOnTop: 50,
} as const;
Utilizando o styled-components
com TypeScript, é possível garantir a execução correta do tema desenvolvido para a aplicação, de forma segura e com developer experience incrível.
Segue um exemplo:
Para isso:
- Crie o tema e defina os seus valores de forma esttica.
- Utilize o
as const
para garantir que todos os valores sejam read only e assim o TypeScript conseguir inferir os tipos e os próprios valores
// theme.ts
export const theme = {
colors: {
// ...
},
breakpoints,
typography,
spacing,
shapes,
layers,
} as const;
Após a criação do arquivo de tema:
- Crie um arquivo de tipagem chamado
styled.d.ts
. - Importe o arquivo de tema e crie um tipo a partir dele utilizando o
typeof
. - Declare um module do
styled-components
. - Exporte o
DefaultTheme
dostyled-components
estendendo o seu tema.
// styled.d.ts
import { theme } from '../theme';
type Theme = typeof theme;
declare module 'styled-components' {
export interface DefaultTheme extends Theme {}
}
TODO: - Adicionar algo como ordem. padrões de css/estilizacao em geral
Utilizando styled-components
, existem diversas formas de acessar suas props
, a fim de padronizar esse acesso, criamos esses dois padrões:
- Em casos onde só será utilizado uma única vez as
props
, utilize da seguinte forma:
Bom - 😃
export const Button = styled.button`
border-radius: ${({ theme }) => theme.shapes.borderRadius.xs};
`;
Ruim -
export const Button = styled.button`
${({ theme }) => css`
border-radius: ${theme.shapes.borderRadius.xs};
`}
`;
- Em casos onde só será necessário utilizar várias vezes as
props
, utilize da seguinte forma:
Bom - 😃
export const Button = styled.button`
${({ theme }) => css`
color: ${theme.colors.text.contrast};
padding: ${theme.spacing.xs} ${theme.spacing.md};
border-radius: ${theme.shapes.borderRadius.xs};
background-color: ${theme.colors.primary.main};
:hover {
background-color: ${theme.colors.primary.dark};
}
:active {
background-color: ${theme.colors.primary.darker};
}
:disabled {
background-color: ${theme.colors.primary.lighter};
}
`}
`;
Ruim -
export const Button = styled.button`
color: ${({ theme }) => colors.text.contrast};
padding: ${({ theme }) => theme.spacing.xs} ${theme.spacing.md};
border-radius: ${({ theme }) => theme.shapes.borderRadius.xs};
background-color: ${({ theme }) => theme.colors.primary.main};
:hover {
background-color: ${({ theme }) => theme.colors.primary.dark};
}
:active {
background-color: ${({ theme }) => theme.colors.primary.darker};
}
:disabled {
background-color: ${({ theme }) => theme.colors.primary.lighter};
}
`;
Durante o desenvolvimento, se torna necessário criar variações de estilos para o mesmo componente, que variam dependendo do valor recebidos em suas props
, exemplo:
<Button color="primary" size="md">
Submit
</Button>
A partir disso, é interessante encapsular essas estilizações, pois facilitar o entendimento do código, auxilia na manutenção e na adição de novas estilizações:
const sizes = {
sm: css`
padding: ${({ theme }) => theme.spacing['2xs']} ${({ theme }) => theme.spacing.sm};
`,
md: css`
padding: ${({ theme }) => theme.spacing.xs} ${({ theme }) => theme.spacing.md};
`,
lg: css`
padding: ${({ theme }) => theme.spacing.sm} ${({ theme }) => theme.spacing.lg};
`,
};
const colors = {
primary: css`
${({ theme }) => css`
color: ${theme.colors.primary.light};
background-color: ${theme.colors.primary.main};
:hover {
background-color: ${theme.colors.primary.dark};
}
:active {
background-color: ${theme.colors.primary.darker};
}
:disabled {
background-color: ${theme.colors.primary.lighter};
}
`};
`,
secondary: css`
${({ theme }) => css`
color: ${theme.colors.secondary.light};
background-color: ${theme.colors.secondary.main};
:hover {
background-color: ${theme.colors.secondary.dark};
}
:active {
background-color: ${theme.colors.secondary.darker};
}
:disabled {
background-color: ${theme.colors.secondary.lighter};
}
`};
`,
};
type ButtonProps = {
size: keyof typeof sizes;
color: keyof typeof colors;
};
export const Button = styled.button<ButtonProps>`
${({ theme, color }) => css`
border-radius: ${theme.shapes.borderRadius.xs};
// ...
${sizes[size]}
${colors[color]}
`}
`;
Por fim, não esqueça de utilizar keyof
typeof
para extrair os possíveis valores de cada variação e utilizar na tipagem das props;
todo
todo
todo
With generics, you can write dynamic and reusable generic blocks of code. Furthermore, you can apply generics in TypeScript to classes, interfaces, and functions. However, you can write anything in the type definition, so follow a list of conventions for generics type:
- K and V are reserved for key-value generic data structures, K for key types and V for value types;
- T and U are reserved for generic data types;
Other generic parameters should have meaningful names:
Bad 😦
function foo<A, S>(attributes: Array<A>, state: S): void {
// ...
}
Good 😃
function foo<Attribute, State>(attributes: Array<Attribute>, state: State): void {
// ...
}
Let's start with the main point, we cannot trust TypeScript types if we allow any
type in the codebase.
From Handbook,
any
type should only be used to migrate a javascript codebase.
Bad 😦
const a: any = 4;
const b: string = a; // no compile time errors
b.toLowerCase(); // runtime error!
If you do not know which type will be received, use the unknown.
todo
Type assertion in TypeScript is the as
syntax and angle-bracket
syntax made available by TypeScript to "assert" any TypeScript identifier to a type of the implementer’s choosing, this can easily be misused and lead to anti-patterns.
Type assertion allows any object to receive any typing.
interface Person {
name: string;
age: number;
occupation: "marketing" | "engineer"; // occupation is a mandatory attribute
}
Bad 😦
const john = {
name: "John",
age: 25,
} as Person; // occupation was not required
// or
const william = <Person>{
name: "William",
age: 25,
}; // occupation was not required
Good 😃
const william: Person = {
name: "William",
age: 25,
occupation: "engineer",
};