Skip to main content

Theme Specification

morfeo's theme specification is strongly inspired by system-ui but it has some important differences. The Theme is an extendible object that describes the design language of your application, it's a composition of slices that contains different elements like colors, gradients, spacings and others:

interface Theme {
radii: Radii;
sizes: Sizes;
fonts: Fonts;
colors: Colors;
shadows: Shadows;
borders: Borders;
spacings: Spacings;
zIndices: ZIndices;
fontSizes: FontSizes;
gradients: Gradients;
opacities: Opacities;
fontWeights: FontWeights;
lineHeights: LineHeights;
breakpoints: BreakPoints;
transitions: Transitions;
borderWidths: BorderWidths;
mediaQueries: MediaQueries;
borderStyles: BorderStyles;
letterSpacings: LetterSpacings;

components: Components;
}

The main difference between morfeo's theme and others is the components slice that will contain the specification of your components.

Properties

In this table, for each slice, are indicated the css properties that will be resolved with values of that slice:

sliceproperties
colorsbg, fill, color, stroke, caretColor, borderColor, outlineColor, borderTopColor, backgroundColor, columnRuleColor, borderLeftColor, borderRightColor, borderBottomColor
radiiborderRadius, borderEndEndRadius, borderTopLeftRadius, borderEndStartRadius, borderTopRightRadius, borderStartEndRadius, borderStartStartRadius, borderBottomLeftRadius, borderBottomRightRadius, corner, cornerEndEnd, cornerTopLeft, cornerEndStart, cornerTopRight, cornerStartEnd, cornerStartStart, cornerBottomLeft, cornerBottomRight,
sizessize, minSize, maxSize width, height, minWidth, maxWidth, minHeight, maxHeight, flexBasis, blockSize, inlineSize, maxBlockSize, minBlockSize, minInlineSize, maxInlineSize
fontsfontFamily
shadowsboxShadow, textShadow, shadow
bordersborder, borderTop, borderLeft, borderRight, borderBlock, borderBottom, borderInline, borderBlockEnd, borderInlineEnd, borderBlockStart, borderInlineStart
spacingsm, mt, ml, mr, mb, mx, my, margin, marginTop, marginRight, marginBottom, marginLeft, marginBlock, marginBlockEnd, marginBlockStart, marginInline, marginInlineEnd, marginInlineStart, p, pt, pl, pr, pb, px, py, padding, paddingTop, paddingLeft, paddingRight, paddingBottom, paddingBlock, paddingBlockEnd, paddingBlockStart, paddingInline, paddingInlineEnd, paddingInlineStart, scrollPadding, scrollPaddingTop, scrollPaddingRight, scrollPaddingBottom, scrollPaddingLeft, inset, insetBlock, insetBlockEnd, insetBlockStart, insetInline, insetInlineEnd, insetInlineStart, top, right, bottom, left, gridGap, gridColumnGap, gridRowGap, gap, columnGap, rowGap
zIndiceszIndex
fontSizesfontSize
gradientsgradient, bgGradient, textGradient
opacitiesopacity
fontWeightsfontWeight
lineHeightslineHeight
breakpoints-
transitionstransition
borderWidthsborderWidth, borderTopWidth, borderLeftWidth, borderRightWidth, borderBlockWidth, borderBottomWidth, borderInlineWidth, borderBlockEndWidth, borderInlineEndWidth, borderBlockStartWidth, borderInlineStartWidth
mediaQueries-
borderStylesborderStyle, borderTopStyle, borderLeftStyle, borderRightStyle, borderBlockStyle, borderBottomStyle, borderInlineStyle, borderBlockEndStyle, borderInlineEndStyle, borderBlockStartStyle, borderInlineStartStyle
letterSpacingsletterSpacing
componentscomponentName, variant

Custom Properties

Some of the properties do not correspond to any CSS rule but they will be resolved to valid CSS rules. This table will clarify better:

propertyresolved
bgbackgroundColor
sizeheight + width
minSize, maxSizeminheight + minWidth, maxHeight + maxWidth
m, mt, ml, mr, mbmargin, marginTop, marginLeft, marginRight, marginBottom
mxmarginLeft + marginRight
mymarginTop + marginBottom
p, pt, pl, pr, pbpadding, paddingTop, paddingLeft, paddingRight, paddingBottom
pxpaddingLeft + paddingRight
pypaddingTop + paddingBottom
gradient, bgGradientbackground
textGradientbackground + backgroundClip + textFillColor
corner, cornerEndEnd, cornerTopLeft, cornerEndStart, cornerTopRight, cornerStartEnd, cornerStartStart, cornerBottomLeft, cornerBottomRight,borderRadius, borderEndEndRadius, borderTopLeftRadius, borderEndStartRadius, borderTopRightRadius, borderStartEndRadius, borderStartStartRadius, borderBottomLeftRadius, borderBottomRightRadius,

componentName, variant will be explained better later in the components slice section.

example

morfeo.setTheme("default", {
colors: { primary: '#fefefe', ... },
spacings: { xxs: '8px', xs: '16px', ... },
...
});
// @morfeo/core | @morfeo/web
const style = morfeo.resolve({ bg: 'primary', px: 'xs' });
// @morfeo/react
const style = useStyle({ bg: 'primary', px: 'xs' });
// @morfeo/svelte
<button use:morfeoStyle={{ bg: 'primary', px: 'xs' }} />

In this case the style { bg: 'primary', px: 'xs' } will be resolved in:

{
"backgroundColor": "#fefefe",
"paddingLeft": "16px",
"paddingRight": "16px"
}

Morfeo Style Object

The object passed to morfeo.resolve (core api), useStyle (react) or use:morfeoStyle (svelte), is called morfeo style object, if your using typescript, this object is described by the type Style:

import { Style } from '@morfeo/spec';

const morfeoStyleObject: Style = {
bg: 'primary',
p: 'm',
...,
}

const style = morfeo.resolve(morfeoStyleObject);
// or if you're using React
// const style = useStyle({ bg: 'primary', px: 'xs' });

The differences between a morfeo style object and a css-in-js object are:

  • the values inside a morfeo style object refer the theme;
  • the keys of a morfeo style object could be aliases

After the parsing by morfeo, a "morfeo style object" is converted into a valid css-in-js that you can use to stylize a component or an html element.

morfeo style objectcss in js object
{ p: 'm' }{ padding: '20px' }
{ color: 'primary' }{ color: '#06f' }
{ componentName: 'Button' }{ backgroundColor: '#06f', paddingLeft: '40px', paddingRight: '40px' }
note

since @morfeo/spec is re-exported by the other morfeo packages, you don't need to install directly @morfeo/spec to use it.

components

motivation

All the other slices are meant to make it easier to follow a design language by using aliases instead of values, so you'd rather use color: danger instead of color: #e74c3c, or padding: s instead of padding: 24px; But this is not enough to ensure that the UI will be consistent. For example:

import { MorfeoComponent, useClassName } from '@morfeo/react';

const Button = props => <MorfeoComponent componentName="Button" {...props} />;

function ConfirmButton({ children }) {
const className = useClassName({ px: 's', bg: 'successs' });
return <Button className={className}>{children}</Button>;
}

function CancelButton({ children }) {
const className = useClassName({
px: 'm',
py: 'xs',
bg: 'danger',
color: 'primary',
});

return <Button className={className}>{children}</Button>;
}
note

we are using @morfeo/react in this example but you can apply the same problem in each language / framework

In this case, every property is following the design language, but the buttons will look completely different, and they'll break the consistency of your app, which is the reason you're using @morfeo (or any other system) in the first place.

The style of your components is part of the design language of your application, that's why in morfeo you can define this style (and other properties) directly inside the theme object.

solution

the component's theme slice is an object where the key is the name of the component and the value is its configuration:

const myTheme = {
...,
components: {
Button: {
style: { height: 'm', px: 's', borderRadius: 'm' },
variants: {
cancel: {
style: { bg: 'danger' },
},
confirm: {
style: { bg: 'success' },
},
},
},
Typography: {
style: { fontFamily: 'regular' },
variants: {
h1: {
style: { fontSize: 'l', fontWeight: 'bold' },
},
h2: {
...,
}
},
},
},
}

Let's repeat the previous example to see the advantages of having components inside the theme object:

function ConfirmButton({ children }) {
return <Button variant="success">{children}</Button>;
}

function CancelButton({ children }) {
return <Button variant="cancel">{children}</Button>;
}

The first advantage we can see is the complete removal of inline-style, the second one is that every component meant to be a button can use the property componentName to ensure it has the same style as all the other buttons.

Of course, every application has more than one button, that's why you can specify inside the theme a list of variants for each component, in this case, we create 2 variants for the Button component: cancel and success; The variants are a specialization of the base component, the style you put inside a variant will be merged with the parent:

// style of Button:
{
height: 'm', px: 's', borderRadius: 'm'
}
// style of success variant
{
height: 'm', px: 's', borderRadius: 'm', bg: 'success'
}
// style of cancel variant
{
height: 'm', px: 's', borderRadius: 'm', bg: 'danger'
}

specification

More about the components slice can be found in the dedicated page here.