diff --git a/packages/orbit-tailwind-preset/src/presets/__fixtures__/BorderRadius.tsx b/packages/orbit-tailwind-preset/src/__fixtures__/BorderRadius.tsx similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/__fixtures__/BorderRadius.tsx rename to packages/orbit-tailwind-preset/src/__fixtures__/BorderRadius.tsx diff --git a/packages/orbit-tailwind-preset/src/presets/__fixtures__/Button.tsx b/packages/orbit-tailwind-preset/src/__fixtures__/Button.tsx similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/__fixtures__/Button.tsx rename to packages/orbit-tailwind-preset/src/__fixtures__/Button.tsx diff --git a/packages/orbit-tailwind-preset/src/presets/__fixtures__/FontSizes.tsx b/packages/orbit-tailwind-preset/src/__fixtures__/FontSizes.tsx similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/__fixtures__/FontSizes.tsx rename to packages/orbit-tailwind-preset/src/__fixtures__/FontSizes.tsx diff --git a/packages/orbit-tailwind-preset/src/presets/__fixtures__/FontWeight.tsx b/packages/orbit-tailwind-preset/src/__fixtures__/FontWeight.tsx similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/__fixtures__/FontWeight.tsx rename to packages/orbit-tailwind-preset/src/__fixtures__/FontWeight.tsx diff --git a/packages/orbit-tailwind-preset/src/presets/__fixtures__/Headings.tsx b/packages/orbit-tailwind-preset/src/__fixtures__/Headings.tsx similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/__fixtures__/Headings.tsx rename to packages/orbit-tailwind-preset/src/__fixtures__/Headings.tsx diff --git a/packages/orbit-tailwind-preset/src/presets/__fixtures__/LineHeight.tsx b/packages/orbit-tailwind-preset/src/__fixtures__/LineHeight.tsx similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/__fixtures__/LineHeight.tsx rename to packages/orbit-tailwind-preset/src/__fixtures__/LineHeight.tsx diff --git a/packages/orbit-tailwind-preset/src/presets/__fixtures__/Screens.tsx b/packages/orbit-tailwind-preset/src/__fixtures__/Screens.tsx similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/__fixtures__/Screens.tsx rename to packages/orbit-tailwind-preset/src/__fixtures__/Screens.tsx diff --git a/packages/orbit-tailwind-preset/src/presets/__fixtures__/Spacings.tsx b/packages/orbit-tailwind-preset/src/__fixtures__/Spacings.tsx similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/__fixtures__/Spacings.tsx rename to packages/orbit-tailwind-preset/src/__fixtures__/Spacings.tsx diff --git a/packages/orbit-tailwind-preset/src/presets/__tests__/__snapshots__/configs.test.ts.snap b/packages/orbit-tailwind-preset/src/__tests__/__snapshots__/configs.test.ts.snap similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/__tests__/__snapshots__/configs.test.ts.snap rename to packages/orbit-tailwind-preset/src/__tests__/__snapshots__/configs.test.ts.snap diff --git a/packages/orbit-tailwind-preset/src/presets/__tests__/button-component.test.tsx b/packages/orbit-tailwind-preset/src/__tests__/button-component.test.tsx similarity index 85% rename from packages/orbit-tailwind-preset/src/presets/__tests__/button-component.test.tsx rename to packages/orbit-tailwind-preset/src/__tests__/button-component.test.tsx index 02bff594b9..ffb9673e6f 100644 --- a/packages/orbit-tailwind-preset/src/presets/__tests__/button-component.test.tsx +++ b/packages/orbit-tailwind-preset/src/__tests__/button-component.test.tsx @@ -2,8 +2,8 @@ import React from "react"; import { getTokens } from "@kiwicom/orbit-design-tokens"; import Button from "../__fixtures__/Button"; -import { render, screen } from "../../testUtils"; -import { firstToUpper } from "../foundation/helpers"; +import { render, screen } from "../testUtils"; +import firstToUpper from "../utils/firstToUpper"; import cssVarsFoundation from "../foundation/cssVarsFoundation"; const testIds = ["primary", "secondary", "info", "success", "warning", "critical"]; diff --git a/packages/orbit-tailwind-preset/src/presets/__tests__/configs.test.ts b/packages/orbit-tailwind-preset/src/__tests__/configs.test.ts similarity index 86% rename from packages/orbit-tailwind-preset/src/presets/__tests__/configs.test.ts rename to packages/orbit-tailwind-preset/src/__tests__/configs.test.ts index 4c00096d43..98caf02b47 100644 --- a/packages/orbit-tailwind-preset/src/presets/__tests__/configs.test.ts +++ b/packages/orbit-tailwind-preset/src/__tests__/configs.test.ts @@ -4,7 +4,7 @@ import orbitPreset from "../.."; describe("orbitPreset", () => { it("should match snapshot", () => { - const cfg = resolveConfig(orbitPreset()); + const cfg = resolveConfig(orbitPreset({ disablePreflight: true })); expect(cfg).toMatchSnapshot(); expect(cfg.corePlugins).not.toContain("preflight"); }); diff --git a/packages/orbit-tailwind-preset/src/presets/__tests__/font.test.tsx b/packages/orbit-tailwind-preset/src/__tests__/font.test.tsx similarity index 97% rename from packages/orbit-tailwind-preset/src/presets/__tests__/font.test.tsx rename to packages/orbit-tailwind-preset/src/__tests__/font.test.tsx index fe7592ca59..dd69cc0e1e 100644 --- a/packages/orbit-tailwind-preset/src/presets/__tests__/font.test.tsx +++ b/packages/orbit-tailwind-preset/src/__tests__/font.test.tsx @@ -1,7 +1,7 @@ import React from "react"; import { defaultTokens } from "@kiwicom/orbit-design-tokens"; -import { render, screen } from "../../testUtils"; +import { render, screen } from "../testUtils"; import FontSizes from "../__fixtures__/FontSizes"; import FontWeight from "../__fixtures__/FontWeight"; import LineHeight from "../__fixtures__/LineHeight"; diff --git a/packages/orbit-tailwind-preset/src/presets/__tests__/headings.test.tsx b/packages/orbit-tailwind-preset/src/__tests__/headings.test.tsx similarity index 87% rename from packages/orbit-tailwind-preset/src/presets/__tests__/headings.test.tsx rename to packages/orbit-tailwind-preset/src/__tests__/headings.test.tsx index 59feb93733..4c8a4b9485 100644 --- a/packages/orbit-tailwind-preset/src/presets/__tests__/headings.test.tsx +++ b/packages/orbit-tailwind-preset/src/__tests__/headings.test.tsx @@ -2,8 +2,8 @@ import React from "react"; import { defaultTokens } from "@kiwicom/orbit-design-tokens"; import Headings from "../__fixtures__/Headings"; -import { render, screen } from "../../testUtils"; -import { firstToUpper } from "../foundation/helpers"; +import { render, screen } from "../testUtils"; +import firstToUpper from "../utils/firstToUpper"; const HEADINGS = [ "title1", diff --git a/packages/orbit-tailwind-preset/src/__tests__/helpers.test.ts b/packages/orbit-tailwind-preset/src/__tests__/helpers.test.ts index af5b7962f2..a11ed2c641 100644 --- a/packages/orbit-tailwind-preset/src/__tests__/helpers.test.ts +++ b/packages/orbit-tailwind-preset/src/__tests__/helpers.test.ts @@ -1,4 +1,4 @@ -import { getComponentLevelTokens } from "../presets/foundation/helpers"; +import getComponentLevelTokens from "../getComponentLevelTokens"; describe("orbit-tailwind-preset", () => { it("should get component level token", () => { diff --git a/packages/orbit-tailwind-preset/src/presets/__tests__/spacing.test.tsx b/packages/orbit-tailwind-preset/src/__tests__/spacing.test.tsx similarity index 92% rename from packages/orbit-tailwind-preset/src/presets/__tests__/spacing.test.tsx rename to packages/orbit-tailwind-preset/src/__tests__/spacing.test.tsx index 9aecc5a0f8..c397514479 100644 --- a/packages/orbit-tailwind-preset/src/presets/__tests__/spacing.test.tsx +++ b/packages/orbit-tailwind-preset/src/__tests__/spacing.test.tsx @@ -1,7 +1,7 @@ import React from "react"; import { defaultTokens } from "@kiwicom/orbit-design-tokens"; -import { render, screen } from "../../testUtils"; +import { render, screen } from "../testUtils"; import Spacings from "../__fixtures__/Spacings"; const SPACINGS = [ diff --git a/packages/orbit-tailwind-preset/src/presets/foundation/cssVarsFoundation.ts b/packages/orbit-tailwind-preset/src/foundation/cssVarsFoundation.ts similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/foundation/cssVarsFoundation.ts rename to packages/orbit-tailwind-preset/src/foundation/cssVarsFoundation.ts diff --git a/packages/orbit-tailwind-preset/src/presets/foundation/getTailwindTheme.ts b/packages/orbit-tailwind-preset/src/foundation/getTailwindTheme.ts similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/foundation/getTailwindTheme.ts rename to packages/orbit-tailwind-preset/src/foundation/getTailwindTheme.ts diff --git a/packages/orbit-tailwind-preset/src/presets/foundation/index.ts b/packages/orbit-tailwind-preset/src/foundation/index.ts similarity index 100% rename from packages/orbit-tailwind-preset/src/presets/foundation/index.ts rename to packages/orbit-tailwind-preset/src/foundation/index.ts diff --git a/packages/orbit-tailwind-preset/src/presets/foundation/helpers.ts b/packages/orbit-tailwind-preset/src/getComponentLevelTokens.ts similarity index 85% rename from packages/orbit-tailwind-preset/src/presets/foundation/helpers.ts rename to packages/orbit-tailwind-preset/src/getComponentLevelTokens.ts index 2c1197781e..30b2ba5a5f 100644 --- a/packages/orbit-tailwind-preset/src/presets/foundation/helpers.ts +++ b/packages/orbit-tailwind-preset/src/getComponentLevelTokens.ts @@ -1,8 +1,6 @@ import { defaultTokens } from "@kiwicom/orbit-design-tokens"; -export const kebabCase = (str: string) => str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); - -export const firstToUpper = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); +import kebabCase from "./utils/kebabCase"; export type ExportedComponentLevelTokens = | "alert" @@ -43,7 +41,7 @@ type ExportedComponentLevelTypes = | "fontWeight" | "lineHeight"; -export const getComponentLevelTokens = +const getComponentLevelTokens = (tokens: typeof defaultTokens = defaultTokens) => (component: ExportedComponentLevelTokens, type: ExportedComponentLevelTypes) => { return Object.keys(tokens).reduce((acc, key) => { @@ -67,3 +65,5 @@ export const getComponentLevelTokens = return acc; }, {}); }; + +export default getComponentLevelTokens; diff --git a/packages/orbit-tailwind-preset/src/index.ts b/packages/orbit-tailwind-preset/src/index.ts index 686a52151b..0a7fa39de5 100644 --- a/packages/orbit-tailwind-preset/src/index.ts +++ b/packages/orbit-tailwind-preset/src/index.ts @@ -1,3 +1,342 @@ -import orbitComponentsPreset from "./presets/components"; +import plugin from "tailwindcss/plugin"; +import { convertHexToRgba, defaultTokens, getTokens } from "@kiwicom/orbit-design-tokens"; +import type { Config } from "tailwindcss"; -export default orbitComponentsPreset; +import orbitFoundationPreset from "./foundation"; +import kebabCase from "./utils/kebabCase"; +import getComponentLevelTokens, { ExportedComponentLevelTokens } from "./getComponentLevelTokens"; +import cssVarsFoundation from "./foundation/cssVarsFoundation"; + +const COLORS: Partial[] = [ + "button", + "buttonLink", + "drawer", + "socialButton", + "alert", + "icon", + "formElement", + "badge", + "tag", + "tab", + "textLink", + "text", + "loading", + "heading", + "countryFlag", +]; + +interface Options { + /** default: true e.g. does not include default normalize styles */ + disablePreflight?: boolean; +} + +const getForegroundColors = (tokens: typeof defaultTokens) => { + const componentTokens = getComponentLevelTokens(tokens); + + return COLORS.reduce((acc, name) => { + if (name === "tab") return acc; + + return { + ...acc, + ...componentTokens(name, "foreground"), + ...componentTokens(name, "foregroundInverted"), + ...componentTokens(name, "foregroundHover"), + ...componentTokens(name, "foregroundActive"), + }; + }, {}); +}; + +const getBackgroundColors = (tokens: typeof defaultTokens) => { + const componentTokens = getComponentLevelTokens(tokens); + + return COLORS.reduce( + (acc, name) => { + return { + ...acc, + ...componentTokens(name, "background"), + ...componentTokens(name, "backgroundHover"), + ...componentTokens(name, "backgroundActive"), + }; + }, + { + "elevation-flat": tokens.elevationFlatBackground, + "elevation-suppressed": tokens.elevationSuppressedBackground, + "elevation-action": tokens.elevationActionBackground, + "elevation-action-active": tokens.elevationActionActiveBackground, + "elevation-fixed": tokens.elevationFixedBackground, + "elevation-fixed-reverse": tokens.elevationFixedReverseBackground, + "elevation-raised": tokens.elevationRaisedBackground, + "elevation-raised-reverse": tokens.elevationRaisedReverseBackground, + "elevation-overlay": tokens.elevationOverlayBackground, + }, + ); +}; + +export default function orbitTailwindPreset(options?: Options): Config { + const { disablePreflight = false } = options || {}; + // make palette colors (foundation colors) defined as css vars and make components tokens inherit it + const tokens = getTokens(cssVarsFoundation); + const componentTokens = getComponentLevelTokens(tokens); + + return { + content: ["auto"], + presets: [orbitFoundationPreset(tokens)], + corePlugins: { + preflight: disablePreflight ? false : undefined, + }, + theme: { + extend: { + fontSize: { + "heading-display": tokens.headingDisplayFontSize, + "heading-display-subtitle": tokens.headingDisplaySubtitleFontSize, + "heading-title1": tokens.headingTitle1FontSize, + "heading-title2": tokens.headingTitle2FontSize, + "heading-title3": tokens.headingTitle3FontSize, + "heading-title4": tokens.headingTitle4FontSize, + "heading-title5": tokens.headingTitle5FontSize, + "heading-title6": tokens.headingTitle6FontSize, + "button-large": tokens.buttonLargeFontSize, + "button-normal": tokens.buttonNormalFontSize, + "button-small": tokens.buttonSmallFontSize, + "form-element-normal": tokens.formElementNormalFontSize, + }, + fontWeight: { + "heading-display": tokens.headingDisplayFontWeight, + "heading-display-subtitle": tokens.headingDisplaySubtitleFontWeight, + "heading-title1": tokens.headingTitle1FontWeight, + "heading-title2": tokens.headingTitle2FontWeight, + "heading-title3": tokens.headingTitle3FontWeight, + "heading-title4": tokens.headingTitle4FontWeight, + "heading-title5": tokens.headingTitle5FontWeight, + "heading-title6": tokens.headingTitle6FontWeight, + }, + lineHeight: { + none: "1", + text: tokens.lineHeightText, + heading: tokens.lineHeightHeading, + "heading-display": tokens.headingDisplayLineHeight, + "heading-display-subtitle": tokens.headingDisplaySubtitleLineHeight, + "heading-title1": tokens.headingTitle1LineHeight, + "heading-title2": tokens.headingTitle2LineHeight, + "heading-title3": tokens.headingTitle3LineHeight, + "heading-title4": tokens.headingTitle4LineHeight, + "heading-title5": tokens.headingTitle5LineHeight, + "heading-title6": tokens.headingTitle6LineHeight, + }, + height: { + "icon-small": tokens.iconSmallSize, + "icon-medium": tokens.iconMediumSize, + "icon-large": tokens.iconLargeSize, + separator: tokens.heightSeparator, + "form-box-small": tokens.formBoxSmallHeight, + "form-box-normal": tokens.formBoxNormalHeight, + "form-box-large": tokens.formBoxLargeHeight, + "country-flag-small": tokens.countryFlagSmallHeight, + "country-flag-medium": tokens.countryFlagMediumHeight, + }, + minHeight: { + "icon-small": tokens.iconSmallSize, + "icon-medium": tokens.iconMediumSize, + "icon-large": tokens.iconLargeSize, + "form-box-small": tokens.formBoxSmallHeight, + "form-box-normal": tokens.formBoxNormalHeight, + "form-box-large": tokens.formBoxLargeHeight, + }, + maxHeight: { + "illustration-extra-small": tokens.illustrationExtraSmallHeight, + "illustration-small": tokens.illustrationSmallHeight, + "illustration-medium": tokens.illustrationMediumHeight, + "illustration-large": tokens.illustrationLargeHeight, + "illustration-display": tokens.illustrationDisplayHeight, + }, + width: { + "icon-small": tokens.iconSmallSize, + "icon-medium": tokens.iconMediumSize, + "icon-large": tokens.iconLargeSize, + "form-box-small": tokens.formBoxSmallHeight, + "form-box-normal": tokens.formBoxNormalHeight, + "form-box-large": tokens.formBoxLargeHeight, + "country-flag-small": tokens.countryFlagSmallWidth, + "country-flag-medium": tokens.countryFlagMediumWidth, + }, + minWidth: { + "icon-small": tokens.iconSmallSize, + "icon-medium": tokens.iconMediumSize, + "icon-large": tokens.iconLargeSize, + "dialog-width": tokens.dialogWidth, + }, + maxWidth: { + "modal-extra-small": defaultTokens.modalExtraSmallWidth, + "modal-small": defaultTokens.modalSmallWidth, + "modal-normal": defaultTokens.modalNormalWidth, + "modal-large": defaultTokens.modalLargeWidth, + "modal-extra-large": defaultTokens.modalExtraLargeWidth, + }, + padding: { + ...componentTokens("button", "padding"), + ...componentTokens("formElement", "padding"), + table: tokens.paddingTable, + "button-padding-xs": tokens.buttonPaddingXSmall, + "button-padding-sm": tokens.buttonPaddingSmall, + "button-padding-md": tokens.buttonPaddingNormal, + "button-padding-lg": tokens.buttonPaddingLarge, + }, + borderRadius: { + "dialog-desktop": tokens.dialogBorderRadiusDesktop, + "dialog-mobile": tokens.dialogBorderRadiusMobile, + "form-box-small": tokens.formBoxSmallHeight, + "form-box-normal": tokens.formBoxNormalHeight, + "form-box-large": tokens.formBoxLargeHeight, + badge: tokens.badgeBorderRadius, + modal: tokens.modalBorderRadius, + "modal-mobile": tokens.modalBorderRadiusMobile, + }, + borderColor: { + ...Object.keys(tokens).reduce((acc, token) => { + if (token.startsWith("borderColor")) { + const name = kebabCase(token).replace("border-color-", ""); + return { ...acc, [name]: tokens[token] }; + } + // eslint-disable-next-line no-param-reassign + acc = { + ...acc, + ...componentTokens(token as ExportedComponentLevelTokens, "borderColor"), + }; + + return acc; + }, {}), + white: tokens.paletteWhite, + "form-element": tokens.formElementBorderColor, + "form-element-disabled": tokens.formElementBorderColorDisabled, + "form-element-hover": tokens.formElementBorderColorHover, + "form-element-active": tokens.formElementBorderColorActive, + "form-element-focus": tokens.formElementBorderColorFocus, + "form-element-error": tokens.formElementBorderColorError, + "form-element-error-hover": tokens.formElementBorderColorErrorHover, + }, + backgroundImage: { + // TODO: remove component prefix and left only bundle-* + "button-bundle-basic-background": tokens.buttonBundleBasicBackground, + "button-bundle-basic-background-hover": tokens.buttonBundleBasicBackgroundHover, + "button-bundle-basic-background-active": tokens.buttonBundleBasicBackgroundActive, + "button-bundle-medium-background": tokens.buttonBundleMediumBackground, + "button-bundle-medium-background-hover": tokens.buttonBundleMediumBackgroundHover, + "button-bundle-medium-background-active": tokens.buttonBundleMediumBackgroundActive, + "button-bundle-top-background": tokens.buttonBundleTopBackground, + "button-bundle-top-background-hover": tokens.buttonBundleTopBackgroundHover, + "button-bundle-top-background-active": tokens.buttonBundleTopBackgroundActive, + "badge-bundle-basic-background": tokens.badgeBundleBasicBackground, + "badge-bundle-medium-background": tokens.badgeBundleMediumBackground, + "badge-bundle-top-background": tokens.badgeBundleTopBackground, + "table-shadow-right": tokens.backgroundTableShadowRight, + "table-shadow-left": tokens.backgroundTableShadowLeft, + "tab-bundle-basic-foreground": tokens.tabBundleBasicForeground, + "tab-bundle-medium-foreground": tokens.tabBundleMediumForeground, + "tab-bundle-top-foreground": tokens.tabBundleTopForeground, + }, + boxShadow: { + "button-focus": tokens.boxShadowButtonFocus, + "button-active": `inset 0 0 6px 3px ${convertHexToRgba(tokens.paletteInkDark, 15)}`, + "button-active-pale": `inset 0 0 6px 3px ${convertHexToRgba(tokens.paletteInkDark, 8)}`, + "country-flag": tokens.countryFlagShadow, + "form-element": tokens.formElementBoxShadow, + "form-element-error": tokens.formElementBoxShadowError, + "form-element-error-hover": tokens.formElementBoxShadowErrorHover, + "form-element-hover": tokens.formElementBoxShadowHover, + "form-element-focus": tokens.formElementFocusBoxShadow, + switch: `inset 0 0 1px 0 rgba(7, 64, 92, 0.1),${tokens.boxShadowAction}`, + "modal-scrolled": `inset 0 1px 0 ${tokens.paletteCloudNormal}, ${tokens.boxShadowFixedReverse}`, + modal: `inset 0 0 0 transparent, ${tokens.boxShadowFixedReverse}`, + }, + keyframes: { + "slow-pulse": { + "0%": { opacity: "1" }, + "50%": { opacity: "0.3" }, + "100%": { opacity: "1" }, + }, + spinner: { + "0%": { transform: "rotate(0deg)" }, + "100%": { transform: "rotate(360deg)" }, + }, + loader: { + "0%": { opacity: "0.3", transform: "translateY(0px)" }, + "20%": { opacity: "1", transform: "translateY(-3px)" }, + "40%": { opacity: "0.3", transform: "translateY(0px)" }, + "100%": { opacity: "0.3", transform: "translateY(0px)" }, + }, + pulse: { + "0%": { transform: "scale(1)" }, + "50%": { transform: "scale(2)" }, + "100%": { transform: "scale(1)" }, + }, + "toast-fade-in": { + "0%": { opacity: "0", transform: "translateY(-20px)" }, + "100%": { opacity: "1", transform: "translateY(0)" }, + }, + "toast-fade-out": { + "0%": { opacity: "1", transform: "translateY(0)" }, + "100%": { opacity: "0", transform: "translateY(-20px)" }, + }, + "toast-light": { + "0%": { transform: "translateX(-100%)" }, + "100%": { transform: "translateX(0%)" }, + }, + "toast-light-rtl": { + "0%": { transform: "translateX(100%)" }, + "100%": { transform: "translateX(0%)" }, + }, + }, + animation: { + "pulse-slow": "slow-pulse 2s ease-in-out 0.5s infinite", + spinner: "spinner 0.75s linear infinite", + loader: "loader 1.25s infinite ease-in-out", + pulse: "pulse 1.5s infinite", + }, + textColor: { + ...Object.entries(getForegroundColors(tokens)).reduce((acc, [key, value]) => { + const name = key.replace("text-", ""); + return { ...acc, [name]: value }; + }, {}), + // overrides for specific WL + "button-primary-foreground": `var(--button-primary-foreground, ${tokens.buttonPrimaryForeground})`, + "button-primary-foreground-hover": `var(--button-primary-foreground-hover, ${tokens.buttonPrimaryForegroundHover})`, + "button-primary-foreground-active": `var(--button-primary-foreground-active, ${tokens.buttonPrimaryForegroundActive})`, + "button-primary-subtle-foreground": `var(--button-primary-subtle-foreground, ${tokens.buttonPrimarySubtleForeground})`, + "button-primary-subtle-foreground-hover": `var(--button-primary-subtle-foreground-hover, ${tokens.buttonPrimarySubtleForegroundHover})`, + "button-primary-subtle-foreground-active": `var(--button-primary-subtle-foreground-active, ${tokens.buttonPrimarySubtleForegroundActive})`, + }, + backgroundColor: { + ...getBackgroundColors(tokens), + // overrides for specific WL + "button-primary-background": `var(--button-primary-background, ${tokens.buttonPrimaryBackground})`, + "button-primary-background-hover": `var(--button-primary-background-hover, ${tokens.buttonPrimaryBackgroundHover})`, + "button-primary-background-active": `var(--button-primary-background-active, ${tokens.buttonPrimaryBackgroundActive})`, + }, + }, + }, + plugins: [ + plugin(({ addVariant, addUtilities }) => { + return ( + addVariant("not-last", "&:not(:last-child)"), + addVariant("not-first", "&:not(:first-child)"), + addVariant("type-even", "&:nth-of-type(even)"), + addVariant("type-odd", "&:nth-of-type(odd)"), + addVariant("target-blank", "&[target='_blank']"), + addVariant( + "safari", + "@supports (-webkit-touch-callout: none) and (not (translate: none))", + ), + addUtilities({ + ".scrollbar-none": { + "-ms-overflow-style": "none", + "scrollbar-width": "none", + "&::-webkit-scrollbar": { + display: "none", + }, + }, + }) + ); + }), + ], + }; +} diff --git a/packages/orbit-tailwind-preset/src/presets/components/index.ts b/packages/orbit-tailwind-preset/src/presets/components/index.ts deleted file mode 100644 index de3632e286..0000000000 --- a/packages/orbit-tailwind-preset/src/presets/components/index.ts +++ /dev/null @@ -1,347 +0,0 @@ -import plugin from "tailwindcss/plugin"; -import { convertHexToRgba, defaultTokens, getTokens } from "@kiwicom/orbit-design-tokens"; -import type { Config } from "tailwindcss"; - -import orbitFoundationPreset from "../foundation"; -import { - kebabCase, - getComponentLevelTokens, - ExportedComponentLevelTokens, -} from "../foundation/helpers"; -import cssVarsFoundation from "../foundation/cssVarsFoundation"; - -const COLORS: Partial[] = [ - "button", - "buttonLink", - "drawer", - "socialButton", - "alert", - "icon", - "formElement", - "badge", - "tag", - "tab", - "textLink", - "text", - "loading", - "heading", - "countryFlag", -]; - -interface Options { - /** default: true e.g. does not include default normalize styles */ - disablePreflight?: boolean; -} - -const getForegroundColors = (tokens: typeof defaultTokens) => { - const componentTokens = getComponentLevelTokens(tokens); - - return COLORS.reduce((acc, name) => { - if (name === "tab") return acc; - - return { - ...acc, - ...componentTokens(name, "foreground"), - ...componentTokens(name, "foregroundInverted"), - ...componentTokens(name, "foregroundHover"), - ...componentTokens(name, "foregroundActive"), - }; - }, {}); -}; - -const getBackgroundColors = (tokens: typeof defaultTokens) => { - const componentTokens = getComponentLevelTokens(tokens); - - return COLORS.reduce( - (acc, name) => { - return { - ...acc, - ...componentTokens(name, "background"), - ...componentTokens(name, "backgroundHover"), - ...componentTokens(name, "backgroundActive"), - }; - }, - { - "elevation-flat": tokens.elevationFlatBackground, - "elevation-suppressed": tokens.elevationSuppressedBackground, - "elevation-action": tokens.elevationActionBackground, - "elevation-action-active": tokens.elevationActionActiveBackground, - "elevation-fixed": tokens.elevationFixedBackground, - "elevation-fixed-reverse": tokens.elevationFixedReverseBackground, - "elevation-raised": tokens.elevationRaisedBackground, - "elevation-raised-reverse": tokens.elevationRaisedReverseBackground, - "elevation-overlay": tokens.elevationOverlayBackground, - }, - ); -}; - -const cfg = (options?: Options): Config => { - const { disablePreflight = true } = options || {}; - // make palette colors (foundation colors) defined as css vars and make components tokens inherit it - const tokens = getTokens(cssVarsFoundation); - const componentTokens = getComponentLevelTokens(tokens); - - return { - content: ["auto"], - presets: [orbitFoundationPreset(tokens)], - corePlugins: { - preflight: disablePreflight ? false : undefined, - }, - theme: { - extend: { - fontSize: { - "heading-display": tokens.headingDisplayFontSize, - "heading-display-subtitle": tokens.headingDisplaySubtitleFontSize, - "heading-title1": tokens.headingTitle1FontSize, - "heading-title2": tokens.headingTitle2FontSize, - "heading-title3": tokens.headingTitle3FontSize, - "heading-title4": tokens.headingTitle4FontSize, - "heading-title5": tokens.headingTitle5FontSize, - "heading-title6": tokens.headingTitle6FontSize, - "button-large": tokens.buttonLargeFontSize, - "button-normal": tokens.buttonNormalFontSize, - "button-small": tokens.buttonSmallFontSize, - "form-element-normal": tokens.formElementNormalFontSize, - }, - fontWeight: { - "heading-display": tokens.headingDisplayFontWeight, - "heading-display-subtitle": tokens.headingDisplaySubtitleFontWeight, - "heading-title1": tokens.headingTitle1FontWeight, - "heading-title2": tokens.headingTitle2FontWeight, - "heading-title3": tokens.headingTitle3FontWeight, - "heading-title4": tokens.headingTitle4FontWeight, - "heading-title5": tokens.headingTitle5FontWeight, - "heading-title6": tokens.headingTitle6FontWeight, - }, - lineHeight: { - none: "1", - text: tokens.lineHeightText, - heading: tokens.lineHeightHeading, - "heading-display": tokens.headingDisplayLineHeight, - "heading-display-subtitle": tokens.headingDisplaySubtitleLineHeight, - "heading-title1": tokens.headingTitle1LineHeight, - "heading-title2": tokens.headingTitle2LineHeight, - "heading-title3": tokens.headingTitle3LineHeight, - "heading-title4": tokens.headingTitle4LineHeight, - "heading-title5": tokens.headingTitle5LineHeight, - "heading-title6": tokens.headingTitle6LineHeight, - }, - height: { - "icon-small": tokens.iconSmallSize, - "icon-medium": tokens.iconMediumSize, - "icon-large": tokens.iconLargeSize, - separator: tokens.heightSeparator, - "form-box-small": tokens.formBoxSmallHeight, - "form-box-normal": tokens.formBoxNormalHeight, - "form-box-large": tokens.formBoxLargeHeight, - "country-flag-small": tokens.countryFlagSmallHeight, - "country-flag-medium": tokens.countryFlagMediumHeight, - }, - minHeight: { - "icon-small": tokens.iconSmallSize, - "icon-medium": tokens.iconMediumSize, - "icon-large": tokens.iconLargeSize, - "form-box-small": tokens.formBoxSmallHeight, - "form-box-normal": tokens.formBoxNormalHeight, - "form-box-large": tokens.formBoxLargeHeight, - }, - maxHeight: { - "illustration-extra-small": tokens.illustrationExtraSmallHeight, - "illustration-small": tokens.illustrationSmallHeight, - "illustration-medium": tokens.illustrationMediumHeight, - "illustration-large": tokens.illustrationLargeHeight, - "illustration-display": tokens.illustrationDisplayHeight, - }, - width: { - "icon-small": tokens.iconSmallSize, - "icon-medium": tokens.iconMediumSize, - "icon-large": tokens.iconLargeSize, - "form-box-small": tokens.formBoxSmallHeight, - "form-box-normal": tokens.formBoxNormalHeight, - "form-box-large": tokens.formBoxLargeHeight, - "country-flag-small": tokens.countryFlagSmallWidth, - "country-flag-medium": tokens.countryFlagMediumWidth, - }, - minWidth: { - "icon-small": tokens.iconSmallSize, - "icon-medium": tokens.iconMediumSize, - "icon-large": tokens.iconLargeSize, - "dialog-width": tokens.dialogWidth, - }, - maxWidth: { - "modal-extra-small": defaultTokens.modalExtraSmallWidth, - "modal-small": defaultTokens.modalSmallWidth, - "modal-normal": defaultTokens.modalNormalWidth, - "modal-large": defaultTokens.modalLargeWidth, - "modal-extra-large": defaultTokens.modalExtraLargeWidth, - }, - padding: { - ...componentTokens("button", "padding"), - ...componentTokens("formElement", "padding"), - table: tokens.paddingTable, - "button-padding-xs": tokens.buttonPaddingXSmall, - "button-padding-sm": tokens.buttonPaddingSmall, - "button-padding-md": tokens.buttonPaddingNormal, - "button-padding-lg": tokens.buttonPaddingLarge, - }, - borderRadius: { - "dialog-desktop": tokens.dialogBorderRadiusDesktop, - "dialog-mobile": tokens.dialogBorderRadiusMobile, - "form-box-small": tokens.formBoxSmallHeight, - "form-box-normal": tokens.formBoxNormalHeight, - "form-box-large": tokens.formBoxLargeHeight, - badge: tokens.badgeBorderRadius, - modal: tokens.modalBorderRadius, - "modal-mobile": tokens.modalBorderRadiusMobile, - }, - borderColor: { - ...Object.keys(tokens).reduce((acc, token) => { - if (token.startsWith("borderColor")) { - const name = kebabCase(token).replace("border-color-", ""); - return { ...acc, [name]: tokens[token] }; - } - // eslint-disable-next-line no-param-reassign - acc = { - ...acc, - ...componentTokens(token as ExportedComponentLevelTokens, "borderColor"), - }; - - return acc; - }, {}), - white: tokens.paletteWhite, - "form-element": tokens.formElementBorderColor, - "form-element-disabled": tokens.formElementBorderColorDisabled, - "form-element-hover": tokens.formElementBorderColorHover, - "form-element-active": tokens.formElementBorderColorActive, - "form-element-focus": tokens.formElementBorderColorFocus, - "form-element-error": tokens.formElementBorderColorError, - "form-element-error-hover": tokens.formElementBorderColorErrorHover, - }, - backgroundImage: { - // TODO: remove component prefix and left only bundle-* - "button-bundle-basic-background": tokens.buttonBundleBasicBackground, - "button-bundle-basic-background-hover": tokens.buttonBundleBasicBackgroundHover, - "button-bundle-basic-background-active": tokens.buttonBundleBasicBackgroundActive, - "button-bundle-medium-background": tokens.buttonBundleMediumBackground, - "button-bundle-medium-background-hover": tokens.buttonBundleMediumBackgroundHover, - "button-bundle-medium-background-active": tokens.buttonBundleMediumBackgroundActive, - "button-bundle-top-background": tokens.buttonBundleTopBackground, - "button-bundle-top-background-hover": tokens.buttonBundleTopBackgroundHover, - "button-bundle-top-background-active": tokens.buttonBundleTopBackgroundActive, - "badge-bundle-basic-background": tokens.badgeBundleBasicBackground, - "badge-bundle-medium-background": tokens.badgeBundleMediumBackground, - "badge-bundle-top-background": tokens.badgeBundleTopBackground, - "table-shadow-right": tokens.backgroundTableShadowRight, - "table-shadow-left": tokens.backgroundTableShadowLeft, - "tab-bundle-basic-foreground": tokens.tabBundleBasicForeground, - "tab-bundle-medium-foreground": tokens.tabBundleMediumForeground, - "tab-bundle-top-foreground": tokens.tabBundleTopForeground, - }, - boxShadow: { - "button-focus": tokens.boxShadowButtonFocus, - "button-active": `inset 0 0 6px 3px ${convertHexToRgba(tokens.paletteInkDark, 15)}`, - "button-active-pale": `inset 0 0 6px 3px ${convertHexToRgba(tokens.paletteInkDark, 8)}`, - "country-flag": tokens.countryFlagShadow, - "form-element": tokens.formElementBoxShadow, - "form-element-error": tokens.formElementBoxShadowError, - "form-element-error-hover": tokens.formElementBoxShadowErrorHover, - "form-element-hover": tokens.formElementBoxShadowHover, - "form-element-focus": tokens.formElementFocusBoxShadow, - switch: `inset 0 0 1px 0 rgba(7, 64, 92, 0.1),${tokens.boxShadowAction}`, - "modal-scrolled": `inset 0 1px 0 ${tokens.paletteCloudNormal}, ${tokens.boxShadowFixedReverse}`, - modal: `inset 0 0 0 transparent, ${tokens.boxShadowFixedReverse}`, - }, - keyframes: { - "slow-pulse": { - "0%": { opacity: "1" }, - "50%": { opacity: "0.3" }, - "100%": { opacity: "1" }, - }, - spinner: { - "0%": { transform: "rotate(0deg)" }, - "100%": { transform: "rotate(360deg)" }, - }, - loader: { - "0%": { opacity: "0.3", transform: "translateY(0px)" }, - "20%": { opacity: "1", transform: "translateY(-3px)" }, - "40%": { opacity: "0.3", transform: "translateY(0px)" }, - "100%": { opacity: "0.3", transform: "translateY(0px)" }, - }, - pulse: { - "0%": { transform: "scale(1)" }, - "50%": { transform: "scale(2)" }, - "100%": { transform: "scale(1)" }, - }, - "toast-fade-in": { - "0%": { opacity: "0", transform: "translateY(-20px)" }, - "100%": { opacity: "1", transform: "translateY(0)" }, - }, - "toast-fade-out": { - "0%": { opacity: "1", transform: "translateY(0)" }, - "100%": { opacity: "0", transform: "translateY(-20px)" }, - }, - "toast-light": { - "0%": { transform: "translateX(-100%)" }, - "100%": { transform: "translateX(0%)" }, - }, - "toast-light-rtl": { - "0%": { transform: "translateX(100%)" }, - "100%": { transform: "translateX(0%)" }, - }, - }, - animation: { - "pulse-slow": "slow-pulse 2s ease-in-out 0.5s infinite", - spinner: "spinner 0.75s linear infinite", - loader: "loader 1.25s infinite ease-in-out", - pulse: "pulse 1.5s infinite", - }, - textColor: { - ...Object.entries(getForegroundColors(tokens)).reduce((acc, [key, value]) => { - const name = key.replace("text-", ""); - return { ...acc, [name]: value }; - }, {}), - // overrides for specific WL - "button-primary-foreground": `var(--button-primary-foreground, ${tokens.buttonPrimaryForeground})`, - "button-primary-foreground-hover": `var(--button-primary-foreground-hover, ${tokens.buttonPrimaryForegroundHover})`, - "button-primary-foreground-active": `var(--button-primary-foreground-active, ${tokens.buttonPrimaryForegroundActive})`, - "button-primary-subtle-foreground": `var(--button-primary-subtle-foreground, ${tokens.buttonPrimarySubtleForeground})`, - "button-primary-subtle-foreground-hover": `var(--button-primary-subtle-foreground-hover, ${tokens.buttonPrimarySubtleForegroundHover})`, - "button-primary-subtle-foreground-active": `var(--button-primary-subtle-foreground-active, ${tokens.buttonPrimarySubtleForegroundActive})`, - }, - backgroundColor: { - ...getBackgroundColors(tokens), - // overrides for specific WL - "button-primary-background": `var(--button-primary-background, ${tokens.buttonPrimaryBackground})`, - "button-primary-background-hover": `var(--button-primary-background-hover, ${tokens.buttonPrimaryBackgroundHover})`, - "button-primary-background-active": `var(--button-primary-background-active, ${tokens.buttonPrimaryBackgroundActive})`, - }, - }, - }, - plugins: [ - plugin(({ addVariant, addUtilities }) => { - return ( - addVariant("not-last", "&:not(:last-child)"), - addVariant("not-first", "&:not(:first-child)"), - addVariant("type-even", "&:nth-of-type(even)"), - addVariant("type-odd", "&:nth-of-type(odd)"), - addVariant("target-blank", "&[target='_blank']"), - addVariant( - "safari", - "@supports (-webkit-touch-callout: none) and (not (translate: none))", - ), - addUtilities({ - ".scrollbar-none": { - "-ms-overflow-style": "none", - "scrollbar-width": "none", - "&::-webkit-scrollbar": { - display: "none", - }, - }, - }) - ); - }), - ], - }; -}; - -export default cfg; diff --git a/packages/orbit-tailwind-preset/src/utils/firstToUpper.ts b/packages/orbit-tailwind-preset/src/utils/firstToUpper.ts new file mode 100644 index 0000000000..a5edfd8af1 --- /dev/null +++ b/packages/orbit-tailwind-preset/src/utils/firstToUpper.ts @@ -0,0 +1,3 @@ +const firstToUpper = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); + +export default firstToUpper; diff --git a/packages/orbit-tailwind-preset/src/utils/kebabCase.ts b/packages/orbit-tailwind-preset/src/utils/kebabCase.ts new file mode 100644 index 0000000000..dd9676239d --- /dev/null +++ b/packages/orbit-tailwind-preset/src/utils/kebabCase.ts @@ -0,0 +1,3 @@ +const kebabCase = (str: string) => str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); + +export default kebabCase;