From 5164bc99360b050e662c2518b0c49d6df2c950f9 Mon Sep 17 00:00:00 2001 From: Kelly Mears Date: Thu, 3 Mar 2022 13:55:39 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BD=E2=80=8D=F0=9F=94=AC?= =?UTF-8?q?=20feature:=20theme.json=20handlers=20(#1199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../@roots/bud-preset-wordpress/package.json | 26 +- .../@roots/bud-preset-wordpress/src/index.ts | 30 +- .../@roots/bud-preset-wordpress/src/theme.ts | 1065 +++++++++++++++++ sources/@roots/sage/package.json | 1 + sources/@roots/sage/src/index.ts | 23 +- sources/@roots/sage/src/sage.preset.ts | 13 +- .../@roots/sage/src/theme/api/themeJson.ts | 46 + .../sage/src/theme/api/useTailwindColors.ts | 31 + sources/@roots/sage/src/theme/extension.ts | 85 ++ sources/@roots/sage/src/theme/index.ts | 6 + sources/@roots/sage/src/theme/plugin.ts | 36 + .../@roots/sage/src/theme/tailwind.adapter.ts | 109 ++ 12 files changed, 1421 insertions(+), 50 deletions(-) create mode 100644 sources/@roots/bud-preset-wordpress/src/theme.ts create mode 100644 sources/@roots/sage/src/theme/api/themeJson.ts create mode 100644 sources/@roots/sage/src/theme/api/useTailwindColors.ts create mode 100644 sources/@roots/sage/src/theme/extension.ts create mode 100644 sources/@roots/sage/src/theme/index.ts create mode 100644 sources/@roots/sage/src/theme/plugin.ts create mode 100644 sources/@roots/sage/src/theme/tailwind.adapter.ts diff --git a/sources/@roots/bud-preset-wordpress/package.json b/sources/@roots/bud-preset-wordpress/package.json index b42c981564..694de8ae4a 100644 --- a/sources/@roots/bud-preset-wordpress/package.json +++ b/sources/@roots/bud-preset-wordpress/package.json @@ -34,14 +34,6 @@ "stylelint-config/", "types/" ], - "main": "./lib/cjs/index.js", - "module": "./lib/esm/index.js", - "types": "./types/index.d.ts", - "exports": { - ".": "./lib/cjs/index.js", - "./eslint-config": "./eslint-config/index.js", - "./stylelint-config": "./stylelint-config/index.js" - }, "bud": { "type": "extension", "peers": [ @@ -55,6 +47,24 @@ "@roots/bud-wordpress-manifests" ] }, + "main": "./lib/cjs/index.js", + "module": "./lib/esm/index.js", + "types": "./types/index.d.ts", + "exports": { + ".": "./lib/cjs/index.js", + "./eslint-config": "./eslint-config/index.js", + "./stylelint-config": "./stylelint-config/index.js" + }, + "typesVersions": { + "*": { + ".": [ + "./types/index.d.ts" + ], + "theme": [ + "./types/theme.d.ts" + ] + } + }, "devDependencies": { "@roots/bud-framework": "workspace:sources/@roots/bud-framework", "@skypack/package-check": "0.2.2", diff --git a/sources/@roots/bud-preset-wordpress/src/index.ts b/sources/@roots/bud-preset-wordpress/src/index.ts index 1d7bf320c4..30a81c658f 100644 --- a/sources/@roots/bud-preset-wordpress/src/index.ts +++ b/sources/@roots/bud-preset-wordpress/src/index.ts @@ -7,34 +7,6 @@ * @see https://roots.io/bud * @see https://github.com/roots/bud * - * @remarks - * - 💁 Composable - Build boss web applications with a modular, configurable build system - * - * - 💪 Modern - Modern framework that scales from a single file to thousands of lines of code - * - * - 🌱 Easy - Low bundle size and fast build times - * - * @remarks - * This preset is a wrapper for the following presets: - * - * - {@link @roots/bud-preset-recommend# | @roots/bud-preset-recommend} - * - * - {@link @roots/bud-react# | @roots/bud-react} - * - * - {@link @roots/bud-wordpress-dependencies# | @roots/bud-wordpress-dependencies} - * - * - {@link @roots/bud-wordpress-externals# | @roots/bud-wordpress-externals} - * - * - {@link @roots/bud-wordpress-manifests# | @roots/bud-wordpress-manifests} - * - * @example - * ```js - * const wp = require('@roots/bud-preset-wordpress') - * - * module.exports = (app: Framework) => { - * app.use(wp) - * } - * ``` * @packageDocumentation */ @@ -63,3 +35,5 @@ type BudWordPressPreset = Extension.Module export const name: BudWordPressPreset['name'] = '@roots/bud-preset-wordpress' + +export * as ThemeJSON from './theme' diff --git a/sources/@roots/bud-preset-wordpress/src/theme.ts b/sources/@roots/bud-preset-wordpress/src/theme.ts new file mode 100644 index 0000000000..7489715747 --- /dev/null +++ b/sources/@roots/bud-preset-wordpress/src/theme.ts @@ -0,0 +1,1065 @@ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsProperties". + */ +export type SettingsProperties = SettingsPropertiesAppearanceTools & + SettingsPropertiesBorder & + SettingsPropertiesColor & + SettingsPropertiesLayout & + SettingsPropertiesSpacing & + SettingsPropertiesTypography & + SettingsPropertiesCustom + +/** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "[a-z0-9]/[a-z0-9]$". + * + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsPropertiesComplete". + * + * This interface was referenced by `SettingsBlocksPropertiesComplete`'s JSON-Schema definition + * via the `patternProperty` "[a-z0-9]/[a-z0-9]$". + */ +export type SettingsPropertiesComplete = SettingsProperties & { + appearanceTools?: unknown + border?: unknown + color?: unknown + layout?: unknown + spacing?: unknown + typography?: unknown + custom?: unknown +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "stylesPropertiesComplete". + */ +export type StylesPropertiesComplete = StylesProperties & { + border?: unknown + color?: unknown + spacing?: unknown + typography?: unknown +} + +/** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "[a-z0-9]/[a-z0-9]$". + * + * This interface was referenced by `StylesBlocksPropertiesComplete`'s JSON-Schema definition + * via the `patternProperty` "[a-z0-9]/[a-z0-9]$". + * + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "stylesPropertiesAndElementsComplete". + */ +export type StylesPropertiesAndElementsComplete = StylesProperties & { + border?: unknown + color?: unknown + spacing?: unknown + typography?: unknown + elements?: StylesElementsPropertiesComplete +} + +export interface JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles { + /** + * JSON schema URI for theme.json. + */ + $schema?: string + + /** + * Version of theme.json to use. + */ + version: 2 + + /** + * Settings for the block editor and individual blocks. These include things like: + * - Which customization options should be available to the user. + * - The default colors, font sizes... available to the user. + * - CSS custom properties and class names used in styles. + * - And the default layout of the editor (widths and available alignments). + */ + settings?: SettingsProperties & { + appearanceTools?: unknown + color?: unknown + layout?: unknown + spacing?: unknown + typography?: unknown + border?: unknown + custom?: unknown + + /** + * Settings defined on a per-block basis. + */ + blocks?: { + /** + * Archive block. Display a monthly archive of your posts. This block has no block-level settings + */ + 'core/archives'?: {} + 'core/audio'?: SettingsPropertiesComplete + 'core/block'?: SettingsPropertiesComplete + 'core/button'?: SettingsPropertiesAppearanceTools & { + /** + * Settings related to borders. + * Gutenberg plugin required. + */ + border?: { + /** + * Allow users to set custom border radius. + * Gutenberg plugin required. + */ + radius?: boolean + [k: string]: unknown + } + [k: string]: unknown + } & SettingsPropertiesColor & + SettingsPropertiesLayout & + SettingsPropertiesSpacing & + SettingsPropertiesTypography & + SettingsPropertiesCustom + 'core/buttons'?: SettingsPropertiesComplete + 'core/calendar'?: SettingsPropertiesComplete + 'core/categories'?: SettingsPropertiesComplete + 'core/code'?: SettingsPropertiesComplete + 'core/column'?: SettingsPropertiesComplete + 'core/columns'?: SettingsPropertiesComplete + 'core/comment-author-name'?: SettingsPropertiesComplete + 'core/comment-author-avatar'?: SettingsPropertiesComplete + 'core/comment-content'?: SettingsPropertiesComplete + 'core/comment-date'?: SettingsPropertiesComplete + 'core/comment-edit-link'?: SettingsPropertiesComplete + 'core/comment-reply-link'?: SettingsPropertiesComplete + 'core/comment-template'?: SettingsPropertiesComplete + 'core/comments-query-loop'?: SettingsPropertiesComplete + 'core/cover'?: SettingsPropertiesComplete + 'core/embed'?: SettingsPropertiesComplete + 'core/file'?: SettingsPropertiesComplete + 'core/freeform'?: SettingsPropertiesComplete + 'core/gallery'?: SettingsPropertiesComplete + 'core/group'?: SettingsPropertiesComplete + 'core/heading'?: SettingsPropertiesComplete + 'core/home-link'?: SettingsPropertiesComplete + 'core/html'?: SettingsPropertiesComplete + 'core/image'?: SettingsPropertiesComplete + 'core/latest-comments'?: SettingsPropertiesComplete + 'core/latest-posts'?: SettingsPropertiesComplete + 'core/list'?: SettingsPropertiesComplete + 'core/loginout'?: SettingsPropertiesComplete + 'core/media-text'?: SettingsPropertiesComplete + 'core/missing'?: SettingsPropertiesComplete + 'core/more'?: SettingsPropertiesComplete + 'core/navigation'?: SettingsPropertiesComplete + 'core/navigation-link'?: SettingsPropertiesComplete + 'core/nextpage'?: SettingsPropertiesComplete + 'core/page-list'?: SettingsPropertiesComplete + 'core/paragraph'?: SettingsPropertiesComplete + 'core/post-author'?: SettingsPropertiesComplete + 'core/post-comments'?: SettingsPropertiesComplete + 'core/post-comments-count'?: SettingsPropertiesComplete + 'core/post-comments-form'?: SettingsPropertiesComplete + 'core/post-comments-link'?: SettingsPropertiesComplete + 'core/post-content'?: SettingsPropertiesComplete + 'core/post-date'?: SettingsPropertiesComplete + 'core/post-excerpt'?: SettingsPropertiesComplete + 'core/post-featured-image'?: SettingsPropertiesComplete + 'core/post-navigation-link'?: SettingsPropertiesComplete + 'core/post-template'?: SettingsPropertiesComplete + 'core/post-terms'?: SettingsPropertiesComplete + 'core/post-title'?: SettingsPropertiesComplete + 'core/preformatted'?: SettingsPropertiesComplete + 'core/pullquote'?: SettingsPropertiesComplete + 'core/query'?: SettingsPropertiesComplete + 'core/query-pagination'?: SettingsPropertiesComplete + 'core/query-pagination-next'?: SettingsPropertiesComplete + 'core/query-pagination-numbers'?: SettingsPropertiesComplete + 'core/query-pagination-previous'?: SettingsPropertiesComplete + 'core/query-title'?: SettingsPropertiesComplete + 'core/quote'?: SettingsPropertiesComplete + 'core/rss'?: SettingsPropertiesComplete + 'core/search'?: SettingsPropertiesComplete + 'core/separator'?: SettingsPropertiesComplete + 'core/shortcode'?: SettingsPropertiesComplete + 'core/site-logo'?: SettingsPropertiesComplete + 'core/site-tagline'?: SettingsPropertiesComplete + 'core/site-title'?: SettingsPropertiesComplete + 'core/social-link'?: SettingsPropertiesComplete + 'core/social-links'?: SettingsPropertiesComplete + 'core/spacer'?: SettingsPropertiesComplete + 'core/table'?: SettingsPropertiesComplete + 'core/table-of-contents'?: SettingsPropertiesComplete + 'core/tag-cloud'?: SettingsPropertiesComplete + 'core/template-part'?: SettingsPropertiesComplete + 'core/term-description'?: SettingsPropertiesComplete + 'core/text-columns'?: SettingsPropertiesComplete + 'core/verse'?: SettingsPropertiesComplete + 'core/video'?: SettingsPropertiesComplete + 'core/widget-area'?: SettingsPropertiesComplete + 'core/legacy-widget'?: SettingsPropertiesComplete + 'core/widget-group'?: SettingsPropertiesComplete + [k: string]: SettingsPropertiesComplete + } + } + + /** + * Organized way to set CSS properties. Styles in the top-level will be added in the `body` selector. + */ + styles?: StylesProperties & { + border?: unknown + color?: unknown + spacing?: unknown + typography?: unknown + + /** + * Styles defined on a per-element basis using the element's selector. + */ + elements?: { + link?: StylesPropertiesComplete + h1?: StylesPropertiesComplete + h2?: StylesPropertiesComplete + h3?: StylesPropertiesComplete + h4?: StylesPropertiesComplete + h5?: StylesPropertiesComplete + h6?: StylesPropertiesComplete + } + + /** + * Styles defined on a per-block basis using the block's selector. + */ + blocks?: { + 'core/archives'?: StylesPropertiesAndElementsComplete + 'core/audio'?: StylesPropertiesAndElementsComplete + 'core/block'?: StylesPropertiesAndElementsComplete + 'core/button'?: StylesPropertiesAndElementsComplete + 'core/buttons'?: StylesPropertiesAndElementsComplete + 'core/calendar'?: StylesPropertiesAndElementsComplete + 'core/categories'?: StylesPropertiesAndElementsComplete + 'core/code'?: StylesPropertiesAndElementsComplete + 'core/column'?: StylesPropertiesAndElementsComplete + 'core/columns'?: StylesPropertiesAndElementsComplete + 'core/comment-author-name'?: StylesPropertiesAndElementsComplete + 'core/comment-author-avatar'?: StylesPropertiesAndElementsComplete + 'core/comment-content'?: StylesPropertiesAndElementsComplete + 'core/comment-date'?: StylesPropertiesAndElementsComplete + 'core/comment-edit-link'?: StylesPropertiesAndElementsComplete + 'core/comment-reply-link'?: StylesPropertiesAndElementsComplete + 'core/comment-template'?: StylesPropertiesAndElementsComplete + 'core/comments-query-loop'?: StylesPropertiesAndElementsComplete + 'core/cover'?: StylesPropertiesAndElementsComplete + 'core/embed'?: StylesPropertiesAndElementsComplete + 'core/file'?: StylesPropertiesAndElementsComplete + 'core/freeform'?: StylesPropertiesAndElementsComplete + 'core/gallery'?: StylesPropertiesAndElementsComplete + 'core/group'?: StylesPropertiesAndElementsComplete + 'core/heading'?: StylesPropertiesAndElementsComplete + 'core/home-link'?: StylesPropertiesAndElementsComplete + 'core/html'?: StylesPropertiesAndElementsComplete + 'core/image'?: StylesPropertiesAndElementsComplete + 'core/latest-comments'?: StylesPropertiesAndElementsComplete + 'core/latest-posts'?: StylesPropertiesAndElementsComplete + 'core/list'?: StylesPropertiesAndElementsComplete + 'core/loginout'?: StylesPropertiesAndElementsComplete + 'core/media-text'?: StylesPropertiesAndElementsComplete + 'core/missing'?: StylesPropertiesAndElementsComplete + 'core/more'?: StylesPropertiesAndElementsComplete + 'core/navigation'?: StylesPropertiesAndElementsComplete + 'core/navigation-link'?: StylesPropertiesAndElementsComplete + 'core/nextpage'?: StylesPropertiesAndElementsComplete + 'core/page-list'?: StylesPropertiesAndElementsComplete + 'core/paragraph'?: StylesPropertiesAndElementsComplete + 'core/post-author'?: StylesPropertiesAndElementsComplete + 'core/post-comments'?: StylesPropertiesAndElementsComplete + 'core/post-comments-count'?: StylesPropertiesAndElementsComplete + 'core/post-comments-form'?: StylesPropertiesAndElementsComplete + 'core/post-comments-link'?: StylesPropertiesAndElementsComplete + 'core/post-content'?: StylesPropertiesAndElementsComplete + 'core/post-date'?: StylesPropertiesAndElementsComplete + 'core/post-excerpt'?: StylesPropertiesAndElementsComplete + 'core/post-featured-image'?: StylesPropertiesAndElementsComplete + 'core/post-navigation-link'?: StylesPropertiesAndElementsComplete + 'core/post-template'?: StylesPropertiesAndElementsComplete + 'core/post-terms'?: StylesPropertiesAndElementsComplete + 'core/post-title'?: StylesPropertiesAndElementsComplete + 'core/preformatted'?: StylesPropertiesAndElementsComplete + 'core/pullquote'?: StylesPropertiesAndElementsComplete + 'core/query'?: StylesPropertiesAndElementsComplete + 'core/query-pagination'?: StylesPropertiesAndElementsComplete + 'core/query-pagination-next'?: StylesPropertiesAndElementsComplete + 'core/query-pagination-numbers'?: StylesPropertiesAndElementsComplete + 'core/query-pagination-previous'?: StylesPropertiesAndElementsComplete + 'core/query-title'?: StylesPropertiesAndElementsComplete + 'core/quote'?: StylesPropertiesAndElementsComplete + 'core/rss'?: StylesPropertiesAndElementsComplete + 'core/search'?: StylesPropertiesAndElementsComplete + 'core/separator'?: StylesPropertiesAndElementsComplete + 'core/shortcode'?: StylesPropertiesAndElementsComplete + 'core/site-logo'?: StylesPropertiesAndElementsComplete + 'core/site-tagline'?: StylesPropertiesAndElementsComplete + 'core/site-title'?: StylesPropertiesAndElementsComplete + 'core/social-link'?: StylesPropertiesAndElementsComplete + 'core/social-links'?: StylesPropertiesAndElementsComplete + 'core/spacer'?: StylesPropertiesAndElementsComplete + 'core/table'?: StylesPropertiesAndElementsComplete + 'core/table-of-contents'?: StylesPropertiesAndElementsComplete + 'core/tag-cloud'?: StylesPropertiesAndElementsComplete + 'core/template-part'?: StylesPropertiesAndElementsComplete + 'core/term-description'?: StylesPropertiesAndElementsComplete + 'core/text-columns'?: StylesPropertiesAndElementsComplete + 'core/verse'?: StylesPropertiesAndElementsComplete + 'core/video'?: StylesPropertiesAndElementsComplete + 'core/widget-area'?: StylesPropertiesAndElementsComplete + 'core/legacy-widget'?: StylesPropertiesAndElementsComplete + 'core/widget-group'?: StylesPropertiesAndElementsComplete + [k: string]: StylesPropertiesAndElementsComplete + } + } + + /** + * Additional metadata for custom templates defined in the templates folder. + */ + customTemplates?: { + /** + * Filename, without extension, of the template in the templates folder. + */ + name: string + + /** + * Title of the template, translatable. + */ + title: string + + /** + * List of post types that can use this custom template. + */ + postTypes?: string[] + }[] + + /** + * Additional metadata for template parts defined in the parts folder. + */ + templateParts?: { + /** + * Filename, without extension, of the template in the parts folder. + */ + name: string + + /** + * Title of the template, translatable. + */ + title?: string + + /** + * The area the template part is used for. Block variations for `header` and `footer` values exist and will be used when the area is set to one of those. + */ + area?: string + }[] + + /** + * An array of pattern slugs to be registered from the Pattern Directory. + */ + patterns?: string[] +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsPropertiesAppearanceTools". + */ +export interface SettingsPropertiesAppearanceTools { + /** + * Setting that enables the following UI tools: + * + * - border: color, radius, style, width + * - color: link + * - spacing: blockGap, margin, padding + * - typography: lineHeight + */ + appearanceTools?: boolean + [k: string]: unknown +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsPropertiesBorder". + */ +export interface SettingsPropertiesBorder { + /** + * Settings related to borders. + */ + border?: { + /** + * Allow users to set custom border colors. + */ + color?: boolean + + /** + * Allow users to set custom border radius. + */ + radius?: boolean + + /** + * Allow users to set custom border styles. + */ + style?: boolean + + /** + * Allow users to set custom border widths. + */ + width?: boolean + } + [k: string]: unknown +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsPropertiesColor". + */ +export interface SettingsPropertiesColor { + /** + * Settings related to colors. + */ + color?: { + /** + * Allow users to set background colors. + */ + background?: boolean + + /** + * Allow users to select custom colors. + */ + custom?: boolean + + /** + * Allow users to create custom duotone filters. + */ + customDuotone?: boolean + + /** + * Allow users to create custom gradients. + */ + customGradient?: boolean + + /** + * Allow users to choose colors from the default gradients. + */ + defaultGradients?: boolean + + /** + * Allow users to choose colors from the default palette. + */ + defaultPalette?: boolean + + /** + * Duotone presets for the duotone picker. + * Doesn't generate classes or properties. + */ + duotone?: { + /** + * Name of the duotone preset, translatable. + */ + name: string + /** + * Kebab-case unique identifier for the duotone preset. + */ + slug: string + /** + * List of colors from dark to light. + */ + colors: string[] + }[] + + /** + * Gradient presets for the gradient picker. + * Generates a single class (`.has-{slug}-background`) and custom property (`--wp--preset--gradient--{slug}`) per preset value. + */ + gradients?: { + /** + * Name of the gradient preset, translatable. + */ + name: string + /** + * Kebab-case unique identifier for the gradient preset. + */ + slug: string + /** + * CSS gradient string. + */ + gradient: string + }[] + + /** + * Allow users to set link colors. + */ + link?: boolean + + /** + * Color palette presets for the color picker. + * Generates three classes (`.has-{slug}-color`, `.has-{slug}-background-color`, and `.has-{slug}-border-color`) and a single custom property (`--wp--preset--color--{slug}`) per preset value. + */ + palette?: { + /** + * Name of the color preset, translatable. + */ + name: string + /** + * Kebab-case unique identifier for the color preset. + */ + slug: string + /** + * CSS hex or rgb(a) string. + */ + color: string + }[] + + /** + * Allow users to set text colors. + */ + text?: boolean + } + [k: string]: unknown +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsPropertiesLayout". + */ +export interface SettingsPropertiesLayout { + /** + * Settings related to layout. + */ + layout?: { + /** + * Sets the max-width of the content. + */ + contentSize?: string + + /** + * Sets the max-width of wide (`.alignwide`) content. + */ + wideSize?: string + } + [k: string]: unknown +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsPropertiesSpacing". + */ +export interface SettingsPropertiesSpacing { + /** + * Settings related to spacing. + */ + spacing?: { + /** + * Enables `--wp--style--block-gap` to be generated from styles.spacing.blockGap. + * A value of `null` instead of `false` further disables layout styles from being generated. + */ + blockGap?: boolean | null + + /** + * Allow users to set a custom margin. + */ + margin?: boolean + + /** + * Allow users to set a custom padding. + */ + padding?: boolean + + /** + * List of units the user can use for spacing values. + */ + units?: string[] + } + [k: string]: unknown +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsPropertiesTypography". + */ +export interface SettingsPropertiesTypography { + /** + * Settings related to typography. + */ + typography?: { + /** + * Allow users to set custom font sizes. + */ + customFontSize?: boolean + + /** + * Allow users to set custom font styles. + */ + fontStyle?: boolean + + /** + * Allow users to set custom font weights. + */ + fontWeight?: boolean + + /** + * Allow users to set custom letter spacing. + */ + letterSpacing?: boolean + + /** + * Allow users to set custom line height. + */ + lineHeight?: boolean + + /** + * Allow users to set custom text decorations. + */ + textDecoration?: boolean + + /** + * Allow users to set custom text transforms. + */ + textTransform?: boolean + + /** + * Enable drop cap. + */ + dropCap?: boolean + + /** + * Font size presets for the font size selector. + * Generates a single class (`.has-{slug}-color`) and custom property (`--wp--preset--font-size--{slug}`) per preset value. + */ + fontSizes?: { + /** + * Name of the font size preset, translatable. + */ + name?: string + /** + * Kebab-case unique identifier for the font size preset. + */ + slug?: string + /** + * CSS font-size value, including units. + */ + size?: string + }[] + + /** + * Font family presets for the font family selector. + * Generates a single custom property (`--wp--preset--font-family--{slug}`) per preset value. + */ + fontFamilies?: { + /** + * Name of the font family preset, translatable. + */ + name?: string + /** + * Kebab-case unique identifier for the font family preset. + */ + slug?: string + /** + * CSS font-family value. + */ + fontFamily?: string + }[] + } + [k: string]: unknown +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsPropertiesCustom". + */ +export interface SettingsPropertiesCustom { + /** + * Generate custom CSS custom properties of the form `--wp--custom--{key}--{nested-key}: {value};`. `camelCased` keys are transformed to `kebab-case` as to follow the CSS property naming schema. Keys at different depth levels are separated by `--`, so keys should not include `--` in the name. + */ + custom?: { + [k: string]: string | number | SettingsCustomAdditionalProperties + } + [k: string]: unknown +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsCustomAdditionalProperties". + */ +export interface SettingsCustomAdditionalProperties { + [k: string]: string | number | SettingsCustomAdditionalProperties +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "stylesProperties". + */ +export interface StylesProperties { + /** + * Border styles. + */ + border?: { + /** + * Sets the `border-color` CSS property. + */ + color?: string + + /** + * Sets the `border-radius` CSS property. + */ + radius?: string + + /** + * Sets the `border-style` CSS property. + */ + style?: string + + /** + * Sets the `border-width` CSS property. + */ + width?: string + } + + /** + * Color styles. + */ + color?: { + /** + * Sets the `background-color` CSS property. + */ + background?: string + + /** + * Sets the `background` CSS property. + */ + gradient?: string + + /** + * Sets the `color` CSS property. + */ + text?: string + } + + /** + * Spacing styles. + */ + spacing?: { + /** + * Sets the `--wp--style--block-gap` CSS custom property when settings.spacing.blockGap is true. + */ + blockGap?: string + + /** + * Margin styles. + */ + margin?: { + /** + * Sets the `margin-top` CSS property. + */ + top?: string + /** + * Sets the `margin-right` CSS property. + */ + right?: string + /** + * Sets the `margin-bottom` CSS property. + */ + bottom?: string + /** + * Sets the `margin-left` CSS property. + */ + left?: string + } + + /** + * Padding styles. + */ + padding?: { + /** + * Sets the `padding-top` CSS property. + */ + top?: string + /** + * Sets the `padding-right` CSS property. + */ + right?: string + /** + * Sets the `padding-bottom` CSS property. + */ + bottom?: string + /** + * Sets the `padding-left` CSS property. + */ + left?: string + } + } + + /** + * Typography styles. + */ + typography?: { + /** + * Sets the `font-family` CSS property. + */ + fontFamily?: string + + /** + * Sets the `font-size` CSS property. + */ + fontSize?: string + + /** + * Sets the `font-style` CSS property. + */ + fontStyle?: string + + /** + * Sets the `font-weight` CSS property. + */ + fontWeight?: string + + /** + * Sets the `letter-spacing` CSS property. + */ + letterSpacing?: string + + /** + * Sets the `line-height` CSS property. + */ + lineHeight?: string + + /** + * Sets the `text-decoration` CSS property. + */ + textDecoration?: string + + /** + * Sets the `text-transform` CSS property. + */ + textTransform?: string + } + [k: string]: unknown +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "stylesElementsPropertiesComplete". + */ +export interface StylesElementsPropertiesComplete { + link?: StylesPropertiesComplete + h1?: StylesPropertiesComplete + h2?: StylesPropertiesComplete + h3?: StylesPropertiesComplete + h4?: StylesPropertiesComplete + h5?: StylesPropertiesComplete + h6?: StylesPropertiesComplete +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "//". + */ +export interface NoName { + [k: string]: unknown +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "settingsBlocksPropertiesComplete". + */ +export interface SettingsBlocksPropertiesComplete { + /** + * Archive block. Display a monthly archive of your posts. This block has no block-level settings + */ + 'core/archives'?: {} + 'core/audio'?: SettingsPropertiesComplete + 'core/block'?: SettingsPropertiesComplete + 'core/button'?: SettingsPropertiesAppearanceTools & { + /** + * Settings related to borders. + * Gutenberg plugin required. + */ + border?: { + /** + * Allow users to set custom border radius. + * Gutenberg plugin required. + */ + radius?: boolean + [k: string]: unknown + } + [k: string]: unknown + } & SettingsPropertiesColor & + SettingsPropertiesLayout & + SettingsPropertiesSpacing & + SettingsPropertiesTypography & + SettingsPropertiesCustom + 'core/buttons'?: SettingsPropertiesComplete + 'core/calendar'?: SettingsPropertiesComplete + 'core/categories'?: SettingsPropertiesComplete + 'core/code'?: SettingsPropertiesComplete + 'core/column'?: SettingsPropertiesComplete + 'core/columns'?: SettingsPropertiesComplete + 'core/comment-author-name'?: SettingsPropertiesComplete + 'core/comment-author-avatar'?: SettingsPropertiesComplete + 'core/comment-content'?: SettingsPropertiesComplete + 'core/comment-date'?: SettingsPropertiesComplete + 'core/comment-edit-link'?: SettingsPropertiesComplete + 'core/comment-reply-link'?: SettingsPropertiesComplete + 'core/comment-template'?: SettingsPropertiesComplete + 'core/comments-query-loop'?: SettingsPropertiesComplete + 'core/cover'?: SettingsPropertiesComplete + 'core/embed'?: SettingsPropertiesComplete + 'core/file'?: SettingsPropertiesComplete + 'core/freeform'?: SettingsPropertiesComplete + 'core/gallery'?: SettingsPropertiesComplete + 'core/group'?: SettingsPropertiesComplete + 'core/heading'?: SettingsPropertiesComplete + 'core/home-link'?: SettingsPropertiesComplete + 'core/html'?: SettingsPropertiesComplete + 'core/image'?: SettingsPropertiesComplete + 'core/latest-comments'?: SettingsPropertiesComplete + 'core/latest-posts'?: SettingsPropertiesComplete + 'core/list'?: SettingsPropertiesComplete + 'core/loginout'?: SettingsPropertiesComplete + 'core/media-text'?: SettingsPropertiesComplete + 'core/missing'?: SettingsPropertiesComplete + 'core/more'?: SettingsPropertiesComplete + 'core/navigation'?: SettingsPropertiesComplete + 'core/navigation-link'?: SettingsPropertiesComplete + 'core/nextpage'?: SettingsPropertiesComplete + 'core/page-list'?: SettingsPropertiesComplete + 'core/paragraph'?: SettingsPropertiesComplete + 'core/post-author'?: SettingsPropertiesComplete + 'core/post-comments'?: SettingsPropertiesComplete + 'core/post-comments-count'?: SettingsPropertiesComplete + 'core/post-comments-form'?: SettingsPropertiesComplete + 'core/post-comments-link'?: SettingsPropertiesComplete + 'core/post-content'?: SettingsPropertiesComplete + 'core/post-date'?: SettingsPropertiesComplete + 'core/post-excerpt'?: SettingsPropertiesComplete + 'core/post-featured-image'?: SettingsPropertiesComplete + 'core/post-navigation-link'?: SettingsPropertiesComplete + 'core/post-template'?: SettingsPropertiesComplete + 'core/post-terms'?: SettingsPropertiesComplete + 'core/post-title'?: SettingsPropertiesComplete + 'core/preformatted'?: SettingsPropertiesComplete + 'core/pullquote'?: SettingsPropertiesComplete + 'core/query'?: SettingsPropertiesComplete + 'core/query-pagination'?: SettingsPropertiesComplete + 'core/query-pagination-next'?: SettingsPropertiesComplete + 'core/query-pagination-numbers'?: SettingsPropertiesComplete + 'core/query-pagination-previous'?: SettingsPropertiesComplete + 'core/query-title'?: SettingsPropertiesComplete + 'core/quote'?: SettingsPropertiesComplete + 'core/rss'?: SettingsPropertiesComplete + 'core/search'?: SettingsPropertiesComplete + 'core/separator'?: SettingsPropertiesComplete + 'core/shortcode'?: SettingsPropertiesComplete + 'core/site-logo'?: SettingsPropertiesComplete + 'core/site-tagline'?: SettingsPropertiesComplete + 'core/site-title'?: SettingsPropertiesComplete + 'core/social-link'?: SettingsPropertiesComplete + 'core/social-links'?: SettingsPropertiesComplete + 'core/spacer'?: SettingsPropertiesComplete + 'core/table'?: SettingsPropertiesComplete + 'core/table-of-contents'?: SettingsPropertiesComplete + 'core/tag-cloud'?: SettingsPropertiesComplete + 'core/template-part'?: SettingsPropertiesComplete + 'core/term-description'?: SettingsPropertiesComplete + 'core/text-columns'?: SettingsPropertiesComplete + 'core/verse'?: SettingsPropertiesComplete + 'core/video'?: SettingsPropertiesComplete + 'core/widget-area'?: SettingsPropertiesComplete + 'core/legacy-widget'?: SettingsPropertiesComplete + 'core/widget-group'?: SettingsPropertiesComplete + [k: string]: SettingsPropertiesComplete +} + +/** + * This interface was referenced by `JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles`'s JSON-Schema + * via the `definition` "stylesBlocksPropertiesComplete". + */ +export interface StylesBlocksPropertiesComplete { + 'core/archives'?: StylesPropertiesAndElementsComplete + 'core/audio'?: StylesPropertiesAndElementsComplete + 'core/block'?: StylesPropertiesAndElementsComplete + 'core/button'?: StylesPropertiesAndElementsComplete + 'core/buttons'?: StylesPropertiesAndElementsComplete + 'core/calendar'?: StylesPropertiesAndElementsComplete + 'core/categories'?: StylesPropertiesAndElementsComplete + 'core/code'?: StylesPropertiesAndElementsComplete + 'core/column'?: StylesPropertiesAndElementsComplete + 'core/columns'?: StylesPropertiesAndElementsComplete + 'core/comment-author-name'?: StylesPropertiesAndElementsComplete + 'core/comment-author-avatar'?: StylesPropertiesAndElementsComplete + 'core/comment-content'?: StylesPropertiesAndElementsComplete + 'core/comment-date'?: StylesPropertiesAndElementsComplete + 'core/comment-edit-link'?: StylesPropertiesAndElementsComplete + 'core/comment-reply-link'?: StylesPropertiesAndElementsComplete + 'core/comment-template'?: StylesPropertiesAndElementsComplete + 'core/comments-query-loop'?: StylesPropertiesAndElementsComplete + 'core/cover'?: StylesPropertiesAndElementsComplete + 'core/embed'?: StylesPropertiesAndElementsComplete + 'core/file'?: StylesPropertiesAndElementsComplete + 'core/freeform'?: StylesPropertiesAndElementsComplete + 'core/gallery'?: StylesPropertiesAndElementsComplete + 'core/group'?: StylesPropertiesAndElementsComplete + 'core/heading'?: StylesPropertiesAndElementsComplete + 'core/home-link'?: StylesPropertiesAndElementsComplete + 'core/html'?: StylesPropertiesAndElementsComplete + 'core/image'?: StylesPropertiesAndElementsComplete + 'core/latest-comments'?: StylesPropertiesAndElementsComplete + 'core/latest-posts'?: StylesPropertiesAndElementsComplete + 'core/list'?: StylesPropertiesAndElementsComplete + 'core/loginout'?: StylesPropertiesAndElementsComplete + 'core/media-text'?: StylesPropertiesAndElementsComplete + 'core/missing'?: StylesPropertiesAndElementsComplete + 'core/more'?: StylesPropertiesAndElementsComplete + 'core/navigation'?: StylesPropertiesAndElementsComplete + 'core/navigation-link'?: StylesPropertiesAndElementsComplete + 'core/nextpage'?: StylesPropertiesAndElementsComplete + 'core/page-list'?: StylesPropertiesAndElementsComplete + 'core/paragraph'?: StylesPropertiesAndElementsComplete + 'core/post-author'?: StylesPropertiesAndElementsComplete + 'core/post-comments'?: StylesPropertiesAndElementsComplete + 'core/post-comments-count'?: StylesPropertiesAndElementsComplete + 'core/post-comments-form'?: StylesPropertiesAndElementsComplete + 'core/post-comments-link'?: StylesPropertiesAndElementsComplete + 'core/post-content'?: StylesPropertiesAndElementsComplete + 'core/post-date'?: StylesPropertiesAndElementsComplete + 'core/post-excerpt'?: StylesPropertiesAndElementsComplete + 'core/post-featured-image'?: StylesPropertiesAndElementsComplete + 'core/post-navigation-link'?: StylesPropertiesAndElementsComplete + 'core/post-template'?: StylesPropertiesAndElementsComplete + 'core/post-terms'?: StylesPropertiesAndElementsComplete + 'core/post-title'?: StylesPropertiesAndElementsComplete + 'core/preformatted'?: StylesPropertiesAndElementsComplete + 'core/pullquote'?: StylesPropertiesAndElementsComplete + 'core/query'?: StylesPropertiesAndElementsComplete + 'core/query-pagination'?: StylesPropertiesAndElementsComplete + 'core/query-pagination-next'?: StylesPropertiesAndElementsComplete + 'core/query-pagination-numbers'?: StylesPropertiesAndElementsComplete + 'core/query-pagination-previous'?: StylesPropertiesAndElementsComplete + 'core/query-title'?: StylesPropertiesAndElementsComplete + 'core/quote'?: StylesPropertiesAndElementsComplete + 'core/rss'?: StylesPropertiesAndElementsComplete + 'core/search'?: StylesPropertiesAndElementsComplete + 'core/separator'?: StylesPropertiesAndElementsComplete + 'core/shortcode'?: StylesPropertiesAndElementsComplete + 'core/site-logo'?: StylesPropertiesAndElementsComplete + 'core/site-tagline'?: StylesPropertiesAndElementsComplete + 'core/site-title'?: StylesPropertiesAndElementsComplete + 'core/social-link'?: StylesPropertiesAndElementsComplete + 'core/social-links'?: StylesPropertiesAndElementsComplete + 'core/spacer'?: StylesPropertiesAndElementsComplete + 'core/table'?: StylesPropertiesAndElementsComplete + 'core/table-of-contents'?: StylesPropertiesAndElementsComplete + 'core/tag-cloud'?: StylesPropertiesAndElementsComplete + 'core/template-part'?: StylesPropertiesAndElementsComplete + 'core/term-description'?: StylesPropertiesAndElementsComplete + 'core/text-columns'?: StylesPropertiesAndElementsComplete + 'core/verse'?: StylesPropertiesAndElementsComplete + 'core/video'?: StylesPropertiesAndElementsComplete + 'core/widget-area'?: StylesPropertiesAndElementsComplete + 'core/legacy-widget'?: StylesPropertiesAndElementsComplete + 'core/widget-group'?: StylesPropertiesAndElementsComplete + [k: string]: StylesPropertiesAndElementsComplete +} diff --git a/sources/@roots/sage/package.json b/sources/@roots/sage/package.json index ebe3870d39..4720e807af 100644 --- a/sources/@roots/sage/package.json +++ b/sources/@roots/sage/package.json @@ -48,6 +48,7 @@ "./client/dom-ready": "./lib/cjs/client/domReady.js", "./eslint-config": "./eslint-config/index.js", "./stylelint-config": "./stylelint-config/index.js", + "./theme/tailwind": "./lib/cjs/theme/tailwind.adapter.js", "./tsconfig": "./tsconfig/tsconfig.json" }, "bud": { diff --git a/sources/@roots/sage/src/index.ts b/sources/@roots/sage/src/index.ts index 0860ba9ec5..285ee30195 100644 --- a/sources/@roots/sage/src/index.ts +++ b/sources/@roots/sage/src/index.ts @@ -11,28 +11,33 @@ */ import {Sage} from './sage.preset' +import * as themeJson from './theme/api/themeJson' +import * as useTailwindColors from './theme/api/useTailwindColors' +import * as ThemeJSON from './theme/extension' +import * as tailwindTheme from './theme/tailwind.adapter' declare module '@roots/bud-framework' { interface Framework { /** - * setPublicPath + * Generate a WordPress `theme.json` * - * @deprecated - * Please remove this function from your config file. It is not needed. - * When Sage 10 exits beta this function call will break your build. - * - * @returns Framework + * @public + */ + themeJson: themeJson.facade + /** + * Emit WordPress `theme.json` color settings using values sourced from + * `tailwind.config.js` (theme.extend.colors) * * @public */ - setPublicPath: ( - publicPath: string | ((publicPath: string) => string), - ) => Framework + useTailwindColors: useTailwindColors.facade } interface Modules { '@roots/sage': typeof Sage + 'wp-theme-json': ThemeJSON.Extension } } export const {name, boot} = Sage +export {tailwindTheme, ThemeJSON} diff --git a/sources/@roots/sage/src/sage.preset.ts b/sources/@roots/sage/src/sage.preset.ts index 174cb3b759..a9a9579629 100644 --- a/sources/@roots/sage/src/sage.preset.ts +++ b/sources/@roots/sage/src/sage.preset.ts @@ -2,10 +2,11 @@ import {Extension} from '@roots/bud-framework' import eventAppClose from './hooks/event.app.close' import eventCompilerDone from './hooks/event.compiler.done' +import * as ThemeJSON from './theme/extension' /** * Sage preset - * + *å * @public */ export const Sage: Extension.Module = { @@ -22,6 +23,11 @@ export const Sage: Extension.Module = { * @public */ boot: async app => { + /** + * Add theme.json generator support + */ + await app.extensions.add(ThemeJSON) + /** * Override output directory for svg assets * `@roots/bud-build` places them, by default, in `svg/` @@ -35,10 +41,7 @@ export const Sage: Extension.Module = { /** * Application paths */ - app.setPath({ - src: 'resources', - dist: 'public', - }) + app.setPath({src: 'resources', dist: 'public'}) /** * Application aliases diff --git a/sources/@roots/sage/src/theme/api/themeJson.ts b/sources/@roots/sage/src/theme/api/themeJson.ts new file mode 100644 index 0000000000..d3c2d9a913 --- /dev/null +++ b/sources/@roots/sage/src/theme/api/themeJson.ts @@ -0,0 +1,46 @@ +import {Framework} from '@roots/bud-framework' +import {Container} from '@roots/container' + +import {WPThemeJson} from '..' + +/** + * Callback function used to configure wordpress `theme.json` + * + * @public + */ +export interface Mutator { + (json: Container): Container< + WPThemeJson['settings'] + > +} + +export interface method { + (callback: Mutator, raw?: boolean): Promise +} + +export interface facade { + (callback: Mutator, raw?: boolean): Framework +} + +export const method: method = async function (callback, raw = false) { + const ctx = this as Framework + + ctx.extensions.get('wp-theme-json').set('when', true) + + if (!callback) + throw new Error(`A callback must be provided to ${ctx.name}.themeJson`) + + if (raw) { + const options = ctx.extensions.get('wp-theme-json').options.all() + + ctx.extensions + .get('wp-theme-json') + .setOptions(ctx.container(callback(options))) + + return ctx + } + + ctx.extensions.get('wp-theme-json').mutateOptions(callback) + + return ctx +} diff --git a/sources/@roots/sage/src/theme/api/useTailwindColors.ts b/sources/@roots/sage/src/theme/api/useTailwindColors.ts new file mode 100644 index 0000000000..2bd0befa21 --- /dev/null +++ b/sources/@roots/sage/src/theme/api/useTailwindColors.ts @@ -0,0 +1,31 @@ +import {Framework} from '@roots/bud-framework' +import {Container} from '@roots/container' + +import {WPThemeJson} from '..' + +export interface method { + (): Promise +} + +export interface facade { + (): Framework +} + +export const method: method = async function () { + const ctx = this as Framework + + const {getPalette, transformPalette} = await import( + '../tailwind.adapter' + ) + const palette = await getPalette( + ctx.path('project', 'tailwind.config.js'), + ) + + await ctx.api.call( + 'themeJson', + (settings: Container) => + settings.set('color.palette', transformPalette(palette)), + ) + + return ctx +} diff --git a/sources/@roots/sage/src/theme/extension.ts b/sources/@roots/sage/src/theme/extension.ts new file mode 100644 index 0000000000..f95ebf09fc --- /dev/null +++ b/sources/@roots/sage/src/theme/extension.ts @@ -0,0 +1,85 @@ +import * as Framework from '@roots/bud-framework' + +import * as themeJson from './api/themeJson' +import * as useTailwindColors from './api/useTailwindColors' +import {Options, ThemeJsonWebpackPlugin} from './plugin' + +/** + * Extension providing a way to manage WordPress `theme.json` values + * from within the context of the build. + * + * @public + */ +export interface Extension + extends Framework.Extension.CompilerPlugin< + ThemeJsonWebpackPlugin, + Options + > { + name: 'wp-theme-json' + api: { + themeJson: themeJson.method + useTailwindColors: useTailwindColors.method + } +} + +/** + * Extension name + * + * @public + */ +export const name: Extension['name'] = 'wp-theme-json' + +/** + * Extension options + * + * @public + */ +export const options: Extension['options'] = { + color: { + custom: false, + customGradient: false, + }, + custom: { + spacing: {}, + typography: {'font-size': {}, 'line-height': {}}, + }, + spacing: { + padding: true, + units: ['px', '%', 'em', 'rem', 'vw', 'vh'], + }, + typography: { + customFontSize: false, + dropCap: false, + }, +} + +/** + * Extension api + * + * @public + */ +export const api: Extension['api'] = { + themeJson: themeJson.method, + useTailwindColors: useTailwindColors.method, +} + +/** + * Extension make + * + * @public + */ +export const make: Extension['make'] = (options, app) => + new ThemeJsonWebpackPlugin({ + path: app.path('project', 'theme.json'), + json: app.json.stringify( + { + $schema: 'https://schemas.wp.org/trunk/theme.json', + version: 2, + settings: options.all(), + }, + null, + 2, + ), + }) + +export const when: Extension['when'] = false diff --git a/sources/@roots/sage/src/theme/index.ts b/sources/@roots/sage/src/theme/index.ts new file mode 100644 index 0000000000..d50ed7bbe5 --- /dev/null +++ b/sources/@roots/sage/src/theme/index.ts @@ -0,0 +1,6 @@ +import type {JSONSchemaForWordPressBlockThemeGlobalSettingsAndStyles as WPThemeJson} from '@roots/bud-preset-wordpress/theme' + +import * as Extension from './extension' +import * as Plugin from './plugin' + +export {Extension, WPThemeJson, Plugin} diff --git a/sources/@roots/sage/src/theme/plugin.ts b/sources/@roots/sage/src/theme/plugin.ts new file mode 100644 index 0000000000..657790962d --- /dev/null +++ b/sources/@roots/sage/src/theme/plugin.ts @@ -0,0 +1,36 @@ +import {fs} from '@roots/bud-support' +import {Compiler, WebpackPluginInstance} from 'webpack' + +/** + * Plugin options + * + * @public + */ +export interface Options { + /** + * JSON contents + */ + json: string + + /** + * Emit path + */ + path: string +} + +/** + * ThemeJSONWebpackPlugin + */ +export class ThemeJsonWebpackPlugin implements WebpackPluginInstance { + public constructor(public options: Options) {} + + public apply(compiler: Compiler) { + compiler.hooks.done.tapPromise(this.constructor.name, async () => { + try { + await fs.writeFile(this.options.path, this.options.json, 'utf8') + } catch (err) { + throw new Error(err) + } + }) + } +} diff --git a/sources/@roots/sage/src/theme/tailwind.adapter.ts b/sources/@roots/sage/src/theme/tailwind.adapter.ts new file mode 100644 index 0000000000..d92f1051b4 --- /dev/null +++ b/sources/@roots/sage/src/theme/tailwind.adapter.ts @@ -0,0 +1,109 @@ +import { + TailwindColorValue, + TailwindValuesColor, +} from 'tailwindcss/tailwind-config' + +import {WPThemeJson} from '.' + +export type WordPressPalette = WPThemeJson['settings']['color']['palette'] +export {TailwindColorValue, TailwindValuesColor} + +/** + * Make a color name from a color label + * + * @param label - color label + * @returns string + * + * @public + */ +interface name { + (label: string): string +} +const name: name = label => + `${label.charAt(0).toUpperCase()}${label.slice(1)}` + +/** + * Make a theme.json palette.color item from a slug and a color + * + * @param slug - color slug + * @param value - color value + * @returns WordPress theme.json color + */ +export interface transform { + (slug: string, color: string): WordPressPalette[any] +} +export const transform: transform = (slug, color) => ({ + name: name(slug), + slug, + color, +}) + +/** + * TailwindCSS palette to WordPress palette reducer + * + * @param acc - WordPress color palette array + * @param param - tuple of tailwind key and color values + * + * @returns + */ +export interface toThemeArray { + ( + acc: WordPressPalette, + [key, value]: [string, TailwindColorValue | TailwindValuesColor], + ): WordPressPalette +} +export const toThemeArray: toThemeArray = (palette, colorValue) => { + const [color] = colorValue + + const normalizeKey = (key: string) => + key !== 'default' ? `${color}-${key}` : color + + const flatten: ( + color: string, + shades: TailwindValuesColor | TailwindColorValue, + ) => Array<[string, string]> = (color, shades) => + typeof shades !== 'string' + ? Object.entries(shades).reduce((all, [variant, color]) => { + return [...all, [normalizeKey(variant), color]] + }, []) + : [[color, shades]] + + return flatten(...colorValue).reduce( + (palette, color) => [...palette, transform(...color)], + palette, + ) +} + +/** + * Get color palette from a tailwind config file + * + * @param path - path to tailwind.config.json + * @returns config.theme.extend.colors + * + * @public + */ +export interface getPalette { + (path: string): Promise +} +export const getPalette = async (path: string) => { + const tailwindImport = await import(path) + const tailwind = tailwindImport?.default ?? tailwindImport + + return tailwind?.theme?.extend?.colors ?? {} +} + +/** + * Transform tailwindcss palette to wordpress theme.json palette + * + * @param palette - from tailwindcss + * + * @public + */ +export interface transformPalette { + (palette: TailwindValuesColor): WordPressPalette +} +export const transformPalette: transformPalette = ( + palette: TailwindValuesColor, +) => { + return Object.entries(palette ?? {}).reduce(toThemeArray, []) +}