Published
Today, we're happy to introduce the first major version RC of VCC UI! Apart from improvements, new components and features, we primarily have been focusing on theming capabilities.
In previous versions, VCC UI exported a couple of constants such as COLORS
which where hard-coded and Volvo specific. Now that it slowly becomes a multi-brand component library, we had to introduce a new, more flexible, concept.
For example, Polestar obviously does not want to use Volvo-branded logos, fonts and colors. That's why VCC UI 1.0.0 introduces a new single source of truth for all design language tokens: brand themes.
Below, we're going through all the crucial parts that have changed with the new theming concept.
The theme is passed by wrapping your app with the ThemeProvider
from VCC UI. It accepts a theme
prop which contains an object of theme properties such as breakpoints, colors or fonts. VCC UI provides default themes for each brand.
import { ThemeProvider } from 'vcc-ui';import volvoTheme from 'vcc-ui/lib/themes/volvo';export default () => (<ThemeProvider theme={volvoTheme}><App /></ThemeProvider>);
One can also extend or modify the theme e.g. adding new breakpoints:
import { getTheme } from 'vcc-ui';const volvoTheme = getTheme({ variant: 'light' });const customTheme = {...volvoTheme,breakpoints: {...volvoTheme.breakpoints,megaLarge: '@media (min-width: 2400px)',},};
Now all of the VCC UI components inside our app will use values from the passed theme. But, apart from using those, we often need to extend component styles which requires e.g. breakpoints or colors from the theme.
There are two ways to get values from the current theme depending on the type of component we're using it.
Within functional components, we can use the useTheme
hook to access theme values.
import { useTheme, Button } from 'vcc-ui';function CustomButton() {const theme = useTheme();return (<Buttonextend={{[theme.breakpoints.fromL]: {color: theme.colors.primaryLight,},}}>Click me</Button>);}
Within class components, we can't use the useTheme
, but have a render-prop component Theme
instead.
import { Theme, Button } from 'vcc-ui';function CustomButton() {return (<Theme>{(theme) => (<Buttonextend={{[theme.breakpoints.fromL]: {color: theme.colors.primaryLight,},}}>Click me</Button>)}</Theme>);}
Since fonts are also included in the theme, the way how fonts are loaded has changed as well. We don't export custom font loading methods anymore, but rather include a plugin that automatically renders fonts on demand. Therefore we introduced 3 new theme properties: fonts
, fontsPath
and fontTypes
.
As soon as we detect a fontFamily
that contains a font name included in the fonts
list, we load and render that specific font using the fontsPath
prefix.
To safely use the fonts, they're also provided as constant values within fontTypes
.
const theme = {fontsPath: '/static/vcc-ui/fonts/',fontTypes: {NOVUM: 'Volvo Novum, Arial',},fonts: [{fontFamily: 'Volvo Novum',fontWeight: 400,src: ['volvo-novum/volvo-novum-regular.woff2','volvo-novum/volvo-novum-regular.woff','volvo-novum/volvo-novum-regular.eot','volvo-novum/volvo-novum-regular.svg',],},],};
Using the theme within your style:
const style = ({ theme }) => ({fontFamily: theme.fontTypes.NOVUM,});
Similar to fonts, the logos are now also part of the theme and are configured using 3 different theme properties: logoImages
, logoImagesPath
, logoTypes
. The logos are referenced similar to the fonts. Images with a 2x
-suffix are automatically treated as retina substitutes and loaded respectively.
const theme = {logoImages: {square: 'volvo-logo.png',square2x: 'volvo-logo-2x.png',wordmark: 'volvo-wordmark.svg',},logoImagesPath: '/static/vcc-ui/images/',logoTypes: {WORDMARK: 'wordmark',SQUARE: 'square',},};
import { useTheme, Logo } from 'vcc-ui';export function SquareLogo() {const { theme } = useTheme();return <Logo type={theme.logoTypes.SQUARE} />;}
Another common use case is RTL styling for languages like Arabic or Hebrew. In the past this was achieved using the ConfigContext.Provider
setting the isRtl
property. Making our theme the single source of truth, the direction is now also part of the theme.
By default, every theme sets direction
to ltr
which is still the more common use case. To enable rtl
flow, one can wrap the components with another ThemeProvider
.
Note: We don't have to spread the full theme again, as the ThemeProvider automatically merges with outer themes.
import { ThemeProvider } from 'vcc-ui'export default () => (<ThemeProvider theme={{ direction: 'rtl' }}><RTLApp /><ThemeProvider>)
The great thing is that we can enable rtl for small subtrees only without affecting the rest of the app.
Apart from the new theming concept, we also changed some components and even added new ones.
Probably the most commonly used components are the base components Block
and Inline
that are used to create layouts. In the past, they're just rendering div
and span
elements which to some extent is fine, but we wanted to make sure the correct display style is also applied.
In vcc-ui 1.0.0, Block renders a div
with display: block
while Inline renders a span
with display: inline
. This might cause some layout changes in existing apps.
Apart from Block and Inline, we're happy to introduce another layout component: Box.
It renders a div
with display: flex
and also implements a default flexbox set that's heavily inspired from React Native:
display: flex;flex-direction: column;flex-grow: 0;flex-shrink: 1;flex-basis: auto;align-self: stretch;
LoadingBar is a component similar to the existing Spinner
but in a linear fashion. It implements a common loading style known from Safari and a couple of websites.
fullWidth
property in favor of a more mobile-first approach. We believe that our components should always adapt to their container instead of setting their size and boundaries themselve. Further reading: https://weser.io/blog/positioning-ui-components.We also updated most of our dependencies.
Especially React and Fela have been updated to the newest versions (react@16.8.x
and fela@10.2.x
) in order to use features such as hooks and the new Context API.
Thus, we recommend everyone to do the same.