Skip to content

Customizing CSS ​

FlipClock.js allows you to easily override the CSS used by a theme. CSS definitions can even accept properties for dynamic declarations, giving you the ability to customize the CSS without the need to override it.

How it works ​

Let's say you want to adjust the animation duration and font size of the clock.

ts
import { css } from 'flipclock';

const declaration = css({
    animationDuration: '100ms',
    fontSize: '3rem'
});

Extending the CSS ​

You can even extend the CSS to create a new declaration based on another.

ts
import { css } from 'flipclock';

const declaration = css({
    animationDuration: '100ms',
    fontSize: '3rem'
}).extend((props) => ({
    // your CSS overrides here.
}));

Custom CSS ​

This uses the default CSS for FlipClock.js as an example. Call useCss to declare the CSS. Then pass the declaration into the theme.

ts
import { clock, flipClock, theme, useCss } from 'flipclock';

/**
 * The FlipClock CSS options.
 * 
 * @public
 */
export type FlipClockCssOptions = {
    borderRadius?: string;
    fontSize?: string;
    fontFamily?: string;
    width?: string;
    height?: string;
    animationDuration?: string;
    animationDelay?: string;
}

/**
 * The FlipClock Theme CSS.
 * 
 * @public
 */
export const css = useCss((options?: FlipClockCssOptions) => ({
    '&': {
        '--border-radius': options?.borderRadius ?? '.15em',
        '--font-size': options?.fontSize ?? '4.5em',
        '--font-family': options?.fontFamily ?? '"Helvetica Neue", Helvetica, sans-serif',
        '--width': options?.width ?? '1em',
        '--height': options?.height ?? '1.45em',
        '--animation-duration': options?.animationDuration ?? '250ms',
        '--animation-delay': options?.animationDelay ?? 'var(--animation-duration)',

        fontFamily: 'var(--font-family)',
        fontSize: 'var(--font-size)',
        userSelect: 'none',
        textAlign: 'center',
        position: 'relative',
        width: '100%',
        display: 'inline-flex',
        boxSizing: 'border-box',
        alignItems: 'flex-end',
        gap: '.25rem'
    },
    '.flip-clock-label': {
        fontSize: '1rem',
        marginBottom: '.5rem',
    },
    '.flip-clock-meridium': {
        lineHeight: '1em',
        top: '50%',
        left: '100%',
        flex: '0',
        width: 'auto',
        textTransform: 'uppercase',
        fontWeight: '200',
        transform: 'translate(.5em, -50%)' 
    },
    '.flip-clock-divider': {
        position: 'relative',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontFamily: 'serif',
        color: '#333',
        height: 'var(--height)',
        '.flip-clock-divider-inner': {
            fontSize: '1.25em' 
        },
        '&[data-value=" "]': {
            minWidth: '1rem'
        },
        '.dark &': {
            color: '#ccc'
        },
    },
    '.flip-clock-group': {
        display: 'flex',
        flexDirection: 'column',
        '+ .flip-clock-group': {
            marginLeft: '1rem' 
        },
        '.flip-clock-group-items': {
            display: 'flex',
            alignItems: 'end',
            gap: '.25rem' 
        }
    },
    '.flip-clock-card': {
        width: 'var(--width)',
        height: 'var(--height)',
        position: 'relative',
        borderRadius: 'var(--border-radius)',
        boxShadow: '0 1.5px 3px rgba(0, 0, 0, 0.24), 0 3px 8px rgba(0, 0, 0, 0.05)',
        fontWeight: 'bold',
        color: '#ccc',
        flexShrink: 0,
        '&:not(.animate)': {
            '.active .flip-clock-card-item-inner': {
                zIndex: '4' 
            },
            '.flip-clock-card-item-inner': {
                '.top, .bottom': {
                    '&:after': {
                        display: 'none' 
                    } 
                } 
            } 
        },
        '.flip-clock-card-item': {
            
        },
        '.flip-clock-card-item-inner': {
            position: 'absolute',
            width: '100%',
            height: '100%',
            '&:first-child': {
                zIndex: '2' 
            },
            '.top, .bottom': {
                width: '100%',
                height: '50%',
                overflow: 'hidden',
                position: 'relative',
                fontSize: '1em',
                background: '#333',
                boxShadow: 'inset 0 0 .2em rgba(0,0,0,.5)',
                '&:after': {
                    content: '" "',
                    display: 'block',
                    position: 'absolute',
                    top: '0',
                    right: '0',
                    bottom: '0',
                    left: '0',
                    overflow: 'hidden' 
                },
                '&:before': {
                    content: '" "',
                    display: 'block',
                    width: '100%',
                    height: '1px',
                    position: 'absolute' 
                } 
            },
            '.top': {
                borderRadius: 'var(--border-radius) var(--border-radius) 0 0',
                lineHeight: 'var(--height)',
                '&:after': {
                    borderRadius: 'var(--border-radius) var(--border-radius) 0 0' 
                },
                '&:before': {
                    background: '#333',
                    opacity: '.4',
                    bottom: '0' 
                } 
            },
            '.bottom': {
                borderRadius: '0 0 var(--border-radius) var(--border-radius)',
                lineHeight: '0',
                '&:after': {
                    borderRadius: '0 0 var(--border-radius) var(--border-radius)' 
                },
                '&:before': {
                    background: '#ccc',
                    opacity: '.1' 
                } 
            } 
        },
        '&.animate': {
            animationDuration: 'var(--animation-duration)',
            animationDelay: 'var(--animation-delay)',
            '.flip-clock-card-item-inner': {
                perspective: '15em' 
            },
            '.top, .bottom, .active, .active > div, .before, .before > div': {
                animationDelay: 'inherit',
                animationFillMode: 'forwards',
                animationDuration: 'inherit',
                animationTimingFunction: 'inherit',
                '&:after': {
                    animationDuration: 'inherit',
                    animationFillMode: 'inherit' 
                } 
            },
            '.before': {
                animationDelay: '0s',
                animationTimingFunction: 'ease-in',
                '.top': {
                    animationName: 'flip-clock-top' 
                },
                '.top:after, .bottom:after': {
                    animationName: 'flip-clock-show-shadow' 
                } 
            },
            '.active': {
                animationTimingFunction: 'ease-out',
                '& > div': {
                    animationName: 'flip-clock-indexing' 
                },
                '.top:after, .bottom:after': {
                    animationName: 'flip-clock-hide-shadow' 
                },
                '.bottom': {
                    animationName: 'flip-clock-bottom' 
                } 
            } 
        },
        '.active': {
            zIndex: '2',
            '.bottom': {
                zIndex: '2',
                transformOrigin: 'top center' 
            } 
        },
        '.before': {
            zIndex: '3',
            '.top': {
                zIndex: '2',
                transformOrigin: 'bottom center',
                '&:after': {
                    background: 'linear-gradient(to bottom, rgba(0,0,0,.1) 0%, rgba(0,0,0,1) 100%)' 
                } 
            },
            '.bottom': {
                '&:after': {
                    background: 'linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,.1) 100%)' 
                } 
            } 
        } 
    },
    '@keyframes flip-clock-indexing': {
        '0%': {
            zIndex: '2' 
        },
        '1%': {
            zIndex: '3' 
        },
        '100%': {
            zIndex: '4' 
        } 
    },
    '@keyframes flip-clock-bottom': {
        '0%': {
            transform: 'rotateX(90deg)' 
        },
        '100%': {
            transform: 'rotateX(0)' 
        } 
    },
    '@keyframes flip-clock-top': {
        '0%': {
            transform: 'rotateX(0)' 
        },
        '100%': {
            transform: 'rotateX(-90deg)' 
        } 
    },
    '@keyframes flip-clock-show-shadow': {
        '0%': {
            opacity: '0' 
        },
        '100%': {
            opacity: '1' 
        } 
    },
    '@keyframes flip-clock-hide-shadow': {
        '0%': {
            opacity: '1' 
        },
        '100%': {
            opacity: '0' 
        } 
    } 
}));

const instance = flipClock({
    face: clock(),
    theme: theme({
        css
    })
});

Using Tradition CSS ​

Nothing prevents you from using traditional CSS. The CSS-in-JS is totally optional. To use CSS file, just import it like you normally would. This works with <style> tags too.

ts
import { clock, flipClock, theme, useCss } from 'flipclock';
import 'style.css';

const instance = flipClock({
    face: clock(),
    theme: theme()
});

Released under the MIT License.