⬅ Back home

Cito Custom Properties

What

Idea

Structure

Q: Why all this? A: Authoring styles from UI point of view is guard railed and robust. Themes can be switched and all of the components adapt to the new theme without any changes required from the usage point of view. This greatly simplifies adding themes and also makes the usage of style related variables consistent.

How to get started

Theming via Javascript

Theming is based on prefers-color-scheme. If more complex theming setup is required, then Javascript based theming is needed on top of prefers-color-scheme query. One solid way is to use custom data-attribute on the root element and then hook styles to that. E.g.:

:root:not([data-theme='light']),
[data-theme='dark'] {...}

CSS

/*
 * Cito Custom Properties 
  * Version 1.6.0
  * Author: ilkka.kuivanen@me.com
*/

/*
 * Ideas for property groups, but not included by default:
    - border-radius
*/

/** Private API: use only within this file. */
:root {
    --cito-custom1-100: oklch(96% 0.0192 269.78);
    --cito-custom1-200: oklch(89% 0.0673 269.78);
    --cito-custom1-300: oklch(77% 0.1118 269.78);
    --cito-custom1-400: oklch(67% 0.1218 269.78);
    --cito-custom1-500: oklch(59% 0.1318 269.78);
    --cito-custom1-600: oklch(48% 0.1418 269.78);
    --cito-custom1-700: oklch(37% 0.1518 269.78);
    --cito-custom1-800: oklch(26% 0.1618 269.78);
    --cito-custom1-900: oklch(19% 0.1718 269.78);

    --cito-blue-100: oklch(96% 0.0192 243);
    --cito-blue-200: oklch(89% 0.0673 243);
    --cito-blue-300: oklch(77% 0.1118 243);
    --cito-blue-400: oklch(67% 0.1218 243);
    --cito-blue-500: oklch(59% 0.1318 243);
    --cito-blue-600: oklch(48% 0.1418 243);
    --cito-blue-700: oklch(37% 0.1518 243);
    --cito-blue-800: oklch(26% 0.1618 243);
    --cito-blue-900: oklch(19% 0.1718 243);

    --cito-red-100: oklch(96% 0.0192 6.18);
    --cito-red-200: oklch(89% 0.0673 6.18);
    --cito-red-300: oklch(77% 0.1673 6.22);
    --cito-red-400: oklch(67% 0.1673 6.22);
    --cito-red-500: oklch(59% 0.1673 6.22);
    --cito-red-600: oklch(48% 0.1673 6.22);
    --cito-red-700: oklch(37% 0.1673 6.22);
    --cito-red-800: oklch(26% 0.1618 6.18);
    --cito-red-900: oklch(19% 0.1718 6.18);

    --cito-yellow-100: oklch(96% 0.0192 73.67);
    --cito-yellow-200: oklch(89% 0.0673 73.67);
    --cito-yellow-300: oklch(77% 0.1437 73.67);
    --cito-yellow-400: oklch(67% 0.1398 74.11);
    --cito-yellow-500: oklch(59% 0.1273 64.8);
    --cito-yellow-600: oklch(48% 0.1267 56.11);
    --cito-yellow-700: oklch(37% 0.1071 56.11);
    --cito-yellow-800: oklch(26% 0.0783 73.67);
    --cito-yellow-900: oklch(19% 0.068 73.67);

    --cito-green-100: oklch(96% 0.0192 141.41);
    --cito-green-200: oklch(89% 0.0673 141.41);
    --cito-green-300: oklch(77% 0.1118 141.41);
    --cito-green-400: oklch(67% 0.1218 141.41);
    --cito-green-500: oklch(59% 0.1318 141.41);
    --cito-green-600: oklch(48% 0.1418 141.41);
    --cito-green-700: oklch(37% 0.1518 141.41);
    --cito-green-800: oklch(26% 0.1618 141.41);
    --cito-green-900: oklch(19% 0.1718 141.41);

    --cito-gray-100: oklch(96% 0.0192 255.78);
    --cito-gray-200: oklch(89% 0.0192 255.78);
    --cito-gray-300: oklch(77% 0.0192 255.78);
    --cito-gray-400: oklch(67% 0.0192 255.78);
    --cito-gray-500: oklch(59% 0.0192 255.78);
    --cito-gray-600: oklch(48% 0.0192 255.78);
    --cito-gray-700: oklch(37% 0.0192 255.78);
    --cito-gray-800: oklch(26% 0.0192 255.78);
    --cito-gray-900: oklch(19% 0.0192 255.78);
}

/** Public API for theme "Light" */
@media (prefers-color-scheme: light) {
    :root {
        --cito-white: oklch(98% 0 0);
        --cito-black: oklch(10% 0 0);

        --cito-upfront-color: var(--cito-gray-900);
        --cito-background-color: var(--cito-white);

        --cito-primary-100: var(--cito-custom1-100);
        --cito-primary-200: var(--cito-custom1-200);
        --cito-primary-300: var(--cito-custom1-300);
        --cito-primary-400: var(--cito-custom1-400);
        --cito-primary-500: var(--cito-custom1-500);
        --cito-primary-600: var(--cito-custom1-600);
        --cito-primary-700: var(--cito-custom1-700);
        --cito-primary-800: var(--cito-custom1-800);
        --cito-primary-900: var(--cito-custom1-900);

        --cito-secondary-100: var(--cito-gray-100);
        --cito-secondary-200: var(--cito-gray-200);
        --cito-secondary-300: var(--cito-gray-300);
        --cito-secondary-400: var(--cito-gray-400);
        --cito-secondary-500: var(--cito-gray-500);
        --cito-secondary-600: var(--cito-gray-600);
        --cito-secondary-700: var(--cito-gray-700);
        --cito-secondary-800: var(--cito-gray-800);
        --cito-secondary-900: var(--cito-gray-900);

        --cito-tertiary-100: var(--cito-cyan-100);
        --cito-tertiary-200: var(--cito-cyan-200);
        --cito-tertiary-300: var(--cito-cyan-300);
        --cito-tertiary-400: var(--cito-cyan-400);
        --cito-tertiary-500: var(--cito-cyan-500);
        --cito-tertiary-600: var(--cito-cyan-600);
        --cito-tertiary-700: var(--cito-cyan-700);
        --cito-tertiary-800: var(--cito-cyan-800);
        --cito-tertiary-900: var(--cito-cyan-900);

        --cito-success-100: var(--cito-green-100);
        --cito-success-200: var(--cito-green-200);
        --cito-success-300: var(--cito-green-300);
        --cito-success-400: var(--cito-green-400);
        --cito-success-500: var(--cito-green-500);
        --cito-success-600: var(--cito-green-600);
        --cito-success-700: var(--cito-green-700);
        --cito-success-800: var(--cito-green-800);
        --cito-success-900: var(--cito-green-900);

        --cito-warning-100: var(--cito-yellow-100);
        --cito-warning-200: var(--cito-yellow-200);
        --cito-warning-300: var(--cito-yellow-300);
        --cito-warning-400: var(--cito-yellow-400);
        --cito-warning-500: var(--cito-yellow-500);
        --cito-warning-600: var(--cito-yellow-600);
        --cito-warning-700: var(--cito-yellow-700);
        --cito-warning-800: var(--cito-yellow-800);
        --cito-warning-900: var(--cito-yellow-900);

        --cito-danger-100: var(--cito-red-100);
        --cito-danger-200: var(--cito-red-200);
        --cito-danger-300: var(--cito-red-300);
        --cito-danger-400: var(--cito-red-400);
        --cito-danger-500: var(--cito-red-500);
        --cito-danger-600: var(--cito-red-600);
        --cito-danger-700: var(--cito-red-700);
        --cito-danger-800: var(--cito-red-800);
        --cito-danger-900: var(--cito-red-900);

        --cito-info-100: var(--cito-blue-100);
        --cito-info-200: var(--cito-blue-200);
        --cito-info-300: var(--cito-blue-300);
        --cito-info-400: var(--cito-blue-400);
        --cito-info-500: var(--cito-blue-500);
        --cito-info-600: var(--cito-blue-600);
        --cito-info-700: var(--cito-blue-700);
        --cito-info-800: var(--cito-blue-800);
        --cito-info-900: var(--cito-blue-900);

        --cito-link-100: var(--cito-blue-100);
        --cito-link-200: var(--cito-blue-200);
        --cito-link-300: var(--cito-blue-300);
        --cito-link-400: var(--cito-blue-400);
        --cito-link-500: var(--cito-blue-500);
        --cito-link-600: var(--cito-blue-600);
        --cito-link-700: var(--cito-blue-700);
        --cito-link-800: var(--cito-blue-800);
        --cito-link-900: var(--cito-blue-900);

        --cito-text-100: var(--cito-secondary-100);
        --cito-text-200: var(--cito-secondary-200);
        --cito-text-300: var(--cito-secondary-300);
        --cito-text-400: var(--cito-secondary-400);
        --cito-text-500: var(--cito-secondary-500);
        --cito-text-600: var(--cito-secondary-600);
        --cito-text-700: var(--cito-secondary-700);
        --cito-text-800: var(--cito-secondary-800);
        --cito-text-900: var(--cito-secondary-900);

        --cito-ui-000: var(--cito-secondary-100);
        --cito-ui-100: var(--cito-secondary-100);
        --cito-ui-200: var(--cito-secondary-200);
        --cito-ui-300: var(--cito-secondary-300);
        --cito-ui-400: var(--cito-secondary-400);
        --cito-ui-500: var(--cito-secondary-500);
        --cito-ui-600: var(--cito-secondary-600);
        --cito-ui-700: var(--cito-secondary-700);
        --cito-ui-800: var(--cito-secondary-800);
        --cito-ui-900: var(--cito-secondary-900);

        /** Component specific exceptions */
        --cito-alert-danger-bgColor: var(--cito-danger-700);
        --cito-alert-danger-borderColor: var(--cito-danger-600);
        --cito-alert-danger-color: var(--cito-danger-100);
    }
}
/** Public API for theme "Dark" */
@media (prefers-color-scheme: dark) {
    :root {
        --cito-white: oklch(98% 0 0);
        --cito-black: oklch(10% 0 0);

        --cito-upfront-color: var(--cito-gray-900);
        --cito-background-color: var(--cito-white);

        --cito-primary-100: var(--cito-blue-900);
        --cito-primary-200: var(--cito-blue-800);
        --cito-primary-300: var(--cito-blue-700);
        --cito-primary-400: var(--cito-blue-600);
        --cito-primary-500: var(--cito-blue-500);
        --cito-primary-600: var(--cito-blue-400);
        --cito-primary-700: var(--cito-blue-300);
        --cito-primary-800: var(--cito-blue-200);
        --cito-primary-900: var(--cito-blue-100);

        --cito-secondary-100: var(--cito-gray-900);
        --cito-secondary-200: var(--cito-gray-800);
        --cito-secondary-300: var(--cito-gray-700);
        --cito-secondary-400: var(--cito-gray-600);
        --cito-secondary-500: var(--cito-gray-500);
        --cito-secondary-600: var(--cito-gray-400);
        --cito-secondary-700: var(--cito-gray-300);
        --cito-secondary-800: var(--cito-gray-200);
        --cito-secondary-900: var(--cito-gray-100);

        --cito-tertiary-100: var(--cito-cyan-900);
        --cito-tertiary-200: var(--cito-cyan-800);
        --cito-tertiary-300: var(--cito-cyan-700);
        --cito-tertiary-400: var(--cito-cyan-600);
        --cito-tertiary-500: var(--cito-cyan-500);
        --cito-tertiary-600: var(--cito-cyan-400);
        --cito-tertiary-700: var(--cito-cyan-300);
        --cito-tertiary-800: var(--cito-cyan-200);
        --cito-tertiary-900: var(--cito-cyan-100);

        --cito-success-100: var(--cito-green-900);
        --cito-success-200: var(--cito-green-800);
        --cito-success-300: var(--cito-green-700);
        --cito-success-400: var(--cito-green-600);
        --cito-success-500: var(--cito-green-500);
        --cito-success-600: var(--cito-green-400);
        --cito-success-700: var(--cito-green-300);
        --cito-success-800: var(--cito-green-200);
        --cito-success-900: var(--cito-green-100);

        --cito-warning-100: var(--cito-yellow-900);
        --cito-warning-200: var(--cito-yellow-800);
        --cito-warning-300: var(--cito-yellow-700);
        --cito-warning-400: var(--cito-yellow-600);
        --cito-warning-500: var(--cito-yellow-500);
        --cito-warning-600: var(--cito-yellow-400);
        --cito-warning-700: var(--cito-yellow-300);
        --cito-warning-800: var(--cito-yellow-200);
        --cito-warning-900: var(--cito-yellow-100);

        --cito-danger-100: var(--cito-red-900);
        --cito-danger-200: var(--cito-red-800);
        --cito-danger-300: var(--cito-red-700);
        --cito-danger-400: var(--cito-red-600);
        --cito-danger-500: var(--cito-red-500);
        --cito-danger-600: var(--cito-red-400);
        --cito-danger-700: var(--cito-red-300);
        --cito-danger-800: var(--cito-red-200);
        --cito-danger-900: var(--cito-red-100);

        --cito-info-100: var(--cito-blue-900);
        --cito-info-200: var(--cito-blue-800);
        --cito-info-300: var(--cito-blue-700);
        --cito-info-400: var(--cito-blue-600);
        --cito-info-500: var(--cito-blue-500);
        --cito-info-600: var(--cito-blue-400);
        --cito-info-700: var(--cito-blue-300);
        --cito-info-800: var(--cito-blue-200);
        --cito-info-900: var(--cito-blue-100);

        --cito-link-100: var(--cito-blue-900);
        --cito-link-200: var(--cito-blue-800);
        --cito-link-300: var(--cito-blue-700);
        --cito-link-400: var(--cito-blue-600);
        --cito-link-500: var(--cito-blue-500);
        --cito-link-600: var(--cito-blue-400);
        --cito-link-700: var(--cito-blue-300);
        --cito-link-800: var(--cito-blue-200);
        --cito-link-900: var(--cito-blue-100);

        --cito-text-100: var(--cito-secondary-100);
        --cito-text-200: var(--cito-secondary-200);
        --cito-text-300: var(--cito-secondary-300);
        --cito-text-400: var(--cito-secondary-400);
        --cito-text-500: var(--cito-secondary-500);
        --cito-text-600: var(--cito-secondary-600);
        --cito-text-700: var(--cito-secondary-700);
        --cito-text-800: var(--cito-secondary-800);
        --cito-text-900: var(--cito-secondary-900);

        --cito-ui-000: var(--cito-secondary-100);
        --cito-ui-100: var(--cito-secondary-100);
        --cito-ui-200: var(--cito-secondary-200);
        --cito-ui-300: var(--cito-secondary-300);
        --cito-ui-400: var(--cito-secondary-400);
        --cito-ui-500: var(--cito-secondary-500);
        --cito-ui-600: var(--cito-secondary-600);
        --cito-ui-700: var(--cito-secondary-700);
        --cito-ui-800: var(--cito-secondary-800);
        --cito-ui-900: var(--cito-secondary-900);

        /** Component specific exceptions */
        --cito-alert-danger-bgColor: var(--cito-danger-100);
        --cito-alert-danger-borderColor: var(--cito-danger-200);
        --cito-alert-danger-color: var(--cito-danger-900);
    }
}

/** Public API for Typography */
:root {
    --cito-font-sans-serif: "Gill Sans", "Helvetica Neue", system-ui,
        -apple-system, sans-serif;
    --cito-font-serif: Georgia, "Times New Roman", Times, serif;
    --cito-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas,
        "Liberation Mono", "Courier New", monospace;

    /** Major Second scale */
    --cito-font-size-000: 0.625rem; /*  10.00 */
    --cito-font-size-100: 0.702rem; /*  11.24 */
    --cito-font-size-200: 0.79rem; /*   12.64 */
    --cito-font-size-300: 0.889rem; /*  14.22 */
    --cito-font-size-400: 1rem; /*      16.00 */
    --cito-font-size-500: 1.125rem; /*  18.00 */
    --cito-font-size-600: 1.266rem; /*  20.25 */
    --cito-font-size-700: 1.424rem; /*  22.78 */
    --cito-font-size-800: 1.602rem; /*  25.63 */
    --cito-font-size-900: 1.802rem; /*  28.83 */
    --cito-font-size-1000: 2.027rem; /* 32.44 */
    --cito-font-size-1100: 2.281rem; /* 36.49 */
    --cito-font-size-1200: 2.566rem; /* 41.05 */
    --cito-font-size-1300: 2.887rem; /* 46.18 */
    --cito-font-size-1400: 3.247rem; /* 51.96 */
    --cito-font-size-1500: 3.653rem; /* 58.45 */
}

/** Public API for Box shadow */
:root {
    --cito-box-shadow-100: oklch(0% 0 0 / 5%) 0 0.0625rem 0.125rem 0;
    --cito-box-shadow-200: oklch(0% 0 0 / 10%) 0 0.0625rem 0.1875rem 0,
        oklch(0% 0 0 / 6%) 0 0.0625rem 0.125rem 0;
    --cito-box-shadow-300: oklch(0% 0 0 / 10%) 0 0.25rem 0.375rem
            calc(-1 * 0.0625rem),
        oklch(0% 0 0 / 6%) 0 0.125rem 0.25rem calc(-1 * 0.0625rem);
    --cito-box-shadow-400: oklch(0% 0 0 / 10%) 0 0.625rem 0.9375rem
            calc(-1 * 0.1875rem),
        oklch(0% 0 0 / 5%) 0 0.25rem 0.375rem calc(-1 * 0.125rem);
    --cito-box-shadow-500: oklch(0% 0 0 / 10%) 0 1.25rem 1.5625rem
            calc(-1 * 0.3125rem),
        rgba(0, 0, 0, 0.04) 0 0.625rem 0.625rem calc(-1 * 0.3125rem);
    --cito-box-shadow-600: oklch(0% 0 0 / 25%) 0 1.5625rem 3.125rem
        calc(-1 * 0.75rem);
    --cito-box-shadow-inset-100: oklch(0% 0 0 / 6%) 0 0.125rem 0.25rem 0 inset;
    --cito-box-shadow-border-100: oklch(0% 0 0 / 5%) 0 0 0 0.0625rem;
    --cito-box-shadow-border-200: oklch(0% 0 0 / 2%) 0 0.0625rem 0.1875rem 0,
        rgba(27, 31, 35, 0.15) 0 0 0 0.0625rem;
}

/** Public API for box shadow inset */
:root {
    --cito-box-shadow-inset-100: oklch(0% 0 0 / 6%) 0 0.125rem 0.25rem 0 inset;
}

/** Public API for box shadow border */
:root {
    --cito-box-shadow-border-100: oklch(0% 0 0 / 5%) 0 0 0 0.0625rem;
    --cito-box-shadow-border-200: oklch(0% 0 0 / 2%) 0 0.0625rem 0.1875rem 0,
        rgba(27, 31, 35, 0.15) 0 0 0 0.0625rem;
}

/** Public API for transitions */
:root {
    --cito-transition-fast: 0.1s;
    --cito-transition-default: 0.25s;
}
From design to dev
Case: Improving task management and note taking for personal use
Contextual Use Scenarios: your favorite UX design driver
Some notes about dark themes
Cito Custom Properties
Cito Theme Controller
Perceived lightness
Atrophy
Strategic misrepresentation
Wrong problem