diff --git a/.changeset/flat-hounds-poke.md b/.changeset/flat-hounds-poke.md new file mode 100644 index 00000000000..20f60fa3ce3 --- /dev/null +++ b/.changeset/flat-hounds-poke.md @@ -0,0 +1,5 @@ +--- +"@astrojs/starlight-tailwind": minor +--- + +Add Tailwind plugin diff --git a/.github/labeler.yml b/.github/labeler.yml index 02cedc805f2..b3592a95ce8 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -14,5 +14,8 @@ i18n: '🌟 core': - packages/starlight/** +'🌟 tailwind': + - packages/tailwind/** + '📚 docs': - docs/** diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41900459fff..b0cc8365881 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,9 +27,8 @@ jobs: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - run: pnpm i - - name: Test packages/starlight - working-directory: ./packages/starlight - run: pnpm test:coverage + - name: Test packages + run: pnpm -r test:coverage pa11y: name: Check for accessibility issues diff --git a/.gitignore b/.gitignore index 34b4dc19b6d..9f880cb7098 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ pnpm-debug.log* # macOS-specific files .DS_Store + +# Vitest +__coverage__/ diff --git a/docs/src/content/docs/guides/css-and-tailwind.mdx b/docs/src/content/docs/guides/css-and-tailwind.mdx new file mode 100644 index 00000000000..fc04b23e8e5 --- /dev/null +++ b/docs/src/content/docs/guides/css-and-tailwind.mdx @@ -0,0 +1,233 @@ +--- +title: CSS & Styling +description: Learn how to style your Starlight site with custom CSS or integrate with Tailwind CSS. +--- + +You can style your Starlight site with custom CSS files or use the Starlight Tailwind plugin. + +## Custom CSS styles + +Customize the styles applied to your Starlight site by providing additional CSS files to modify or extend Starlight’s default styles. + +1. Add a CSS file to your `src/` directory. + For example, you could override Starlight’s default blue accent hue to purple: + + ```css + /* src/styles/custom.css */ + :root { + --sl-hue-accent: 270; + } + ``` + +2. Add the path to your CSS file to Starlight’s `customCss` array in `astro.config.mjs`: + + ```js + // astro.config.mjs + import { defineConfig } from 'astro/config'; + import starlight from '@astrojs/starlight'; + + export default defineConfig({ + integrations: [ + starlight({ + title: 'Docs With Custom CSS', + customCss: [ + // Relative path to your custom CSS file + './src/styles/custom.css', + ], + }), + ], + }); + ``` + +You can see all the CSS custom properties used by Starlight that you can set to customize your site in the [`props.css` file on GitHub](https://github.com/withastro/starlight/blob/main/packages/starlight/style/props.css). + +## Tailwind CSS + +Tailwind CSS support in Astro projects is provided by the [Astro Tailwind integration](https://docs.astro.build/en/guides/integrations-guide/tailwind/). +Starlight provides a complementary Tailwind plugin to help configure Tailwind for compatibility with Starlight’s styles. + +The Starlight Tailwind plugin applies the following configuration: + +- Configures Tailwind’s `dark:` variants to work with Starlight’s dark mode. +- Uses Tailwind [theme colors and fonts](#styling-starlight-with-tailwind) in Starlight’s UI. +- Disables Tailwind’s [Preflight](https://tailwindcss.com/docs/preflight) reset styles while selectively restoring essential parts of Preflight required for Tailwind’s border utility classes. + +### Create a new project with Tailwind + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +Start a new Starlight project with Tailwind CSS pre-configured using `create astro`: + + + + +```sh +npm create astro@latest -- --template withastro/starlight/examples/tailwind +``` + + + + +```sh +pnpm create astro --template withastro/starlight/examples/tailwind +``` + + + + +```sh +yarn create astro --template withastro/starlight/examples/tailwind +``` + + + + +### Add Tailwind to an existing project + +If you already have a Starlight site and want to add Tailwind CSS, follow these steps. + +1. Add Astro’s Tailwind integration: + + + + + + ```sh + npx astro add tailwind + ``` + + + + + + ```sh + pnpm astro add tailwind + ``` + + + + + + ```sh + yarn astro add tailwind + ``` + + + + + +2. Install the Starlight Tailwind plugin: + + + + + + ```sh + npm install @astrojs/starlight-tailwind + ``` + + + + + + ```sh + pnpm install @astrojs/starlight-tailwind + ``` + + + + + + ```sh + yarn add @astrojs/starlight-tailwind + ``` + + + + + +3. Create a CSS file for Tailwind’s base styles, for example at `src/tailwind.css`: + + ```css + /* src/tailwind.css */ + @tailwind base; + @tailwind components; + @tailwind utilities; + ``` + +4. Update your Astro config file to use your Tailwind base styles and disable the default base styles: + + ```js {11-12,16-17} + // astro.config.mjs + import { defineConfig } from 'astro/config'; + import starlight from '@astrojs/starlight'; + import tailwind from '@astrojs/tailwind'; + + export default defineConfig({ + integrations: [ + starlight({ + title: 'Docs with Tailwind', + customCss: [ + // Path to your Tailwind base styles: + './src/tailwind.css', + ], + }), + tailwind({ + // Disable the default base styles: + applyBaseStyles: false, + }), + ], + }); + ``` + +5. Add the Starlight Tailwind plugin to `tailwind.config.cjs`: + + ```js ins={2,7} + // tailwind.config.cjs + const starlightPlugin = require('@astrojs/starlight-tailwind'); + + /** @type {import('tailwindcss').Config} */ + module.exports = { + content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], + plugins: [starlightPlugin()], + }; + ``` + +### Styling Starlight with Tailwind + +Starlight will use values from your [Tailwind theme config](https://tailwindcss.com/docs/theme) in its UI. + +If set, the following options will override Starlight’s default styles: + +- `colors.accent` — used for links and current item highlighting +- `colors.gray` — used for background colors and borders +- `fontFamily.sans` — used for UI and content text +- `fontFamily.mono` — used for code examples + +```js {12,14,18,20} +// tailwind.config.cjs +const starlightPlugin = require('@astrojs/starlight-tailwind'); +const colors = require('tailwindcss/colors'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], + theme: { + extend: { + colors: { + // Your preferred accent color. Indigo is closest to Starlight’s defaults. + accent: colors.indigo, + // Your preferred gray scale. Zinc is closest to Starlight’s defaults. + gray: colors.zinc, + }, + fontFamily: { + // Your preferred text font. Starlight uses a system font stack by default. + sans: ['"Atkinson Hyperlegible"'], + // Your preferred code font. Starlight uses system monospace fonts by default. + mono: ['"IBM Plex Mono"'], + }, + }, + }, + plugins: [starlightPlugin()], +}; +``` diff --git a/docs/src/content/docs/guides/customization.mdx b/docs/src/content/docs/guides/customization.mdx index d337b260879..733ff6ceb9a 100644 --- a/docs/src/content/docs/guides/customization.mdx +++ b/docs/src/content/docs/guides/customization.mdx @@ -1,6 +1,6 @@ --- title: Customizing Starlight -description: Learn how to make your Starlight site your own with custom styles, fonts, and more. +description: Learn how to make your Starlight site your own with your logo, custom fonts, landing page design and more. --- import { Tabs, TabItem } from '@astrojs/starlight/components'; @@ -263,42 +263,6 @@ hero: --- ``` -## Custom CSS styles - -Customize the styles applied to your Starlight site by providing additional CSS files to modify or extend Starlight’s default styles. - -1. Add a CSS file to your `src/` directory. - For example, you could override Starlight’s default blue accent hue to purple: - - ```css - /* src/styles/custom.css */ - :root { - --sl-hue-accent: 270; - } - ``` - -2. Add the path to your CSS file to Starlight’s `customCss` array in `astro.config.mjs`: - - ```js - // astro.config.mjs - import { defineConfig } from 'astro/config'; - import starlight from '@astrojs/starlight'; - - export default defineConfig({ - integrations: [ - starlight({ - title: 'Docs With Custom CSS', - customCss: [ - // Relative path to your custom CSS file - './src/styles/custom.css', - ], - }), - ], - }); - ``` - -You can see all the CSS custom properties used by Starlight that you can set to customize your site in the [`props.css` file on GitHub](https://github.com/withastro/starlight/blob/main/packages/starlight/style/props.css). - ## Custom fonts By default, Starlight uses sans-serif fonts available on a user’s local device for all text. @@ -426,7 +390,7 @@ It provides npm modules you can install for the fonts you want to use and includ ### Use fonts -To apply the font you set up to your site, use your chosen font’s name in a custom CSS file. +To apply the font you set up to your site, use your chosen font’s name in a [custom CSS file](/guides/css-and-tailwind/#custom-css-styles). For example, to override Starlight’s default font everywhere, set the `--sl-font` custom property: ```css @@ -448,4 +412,4 @@ main { } ``` -Follow the [custom CSS instructions](#custom-css-styles) to add your styles to your site. +Follow the [custom CSS instructions](/guides/css-and-tailwind/#custom-css-styles) to add your styles to your site. diff --git a/examples/tailwind/.gitignore b/examples/tailwind/.gitignore new file mode 100644 index 00000000000..6240da8b10b --- /dev/null +++ b/examples/tailwind/.gitignore @@ -0,0 +1,21 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store diff --git a/examples/tailwind/.vscode/extensions.json b/examples/tailwind/.vscode/extensions.json new file mode 100644 index 00000000000..22a15055d63 --- /dev/null +++ b/examples/tailwind/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/examples/tailwind/.vscode/launch.json b/examples/tailwind/.vscode/launch.json new file mode 100644 index 00000000000..d6422097621 --- /dev/null +++ b/examples/tailwind/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/examples/tailwind/README.md b/examples/tailwind/README.md new file mode 100644 index 00000000000..39debfb78e8 --- /dev/null +++ b/examples/tailwind/README.md @@ -0,0 +1,52 @@ +# Starlight Starter Kit: Tailwind + +``` +npm create astro@latest -- --template withastro/starlight/examples/tailwind +``` + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics) + +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! + +## 🚀 Project Structure + +Inside of your Astro + Starlight project, you'll see the following folders and files: + +``` +. +├── public/ +├── src/ +│ ├── assets/ +│ ├── content/ +│ │ ├── docs/ +│ │ └── config.ts +│ └── env.d.ts +├── astro.config.mjs +├── package.json +├── tailwind.config.cjs +└── tsconfig.json +``` + +Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. + +Images can be added to `src/assets/` and embedded in Markdown with a relative link. + +Static assets, like favicons, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:3000` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | +| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `npm run astro -- --help` | Get help using the Astro CLI | + +## 👀 Want to learn more? + +Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). diff --git a/examples/tailwind/astro.config.mjs b/examples/tailwind/astro.config.mjs new file mode 100644 index 00000000000..63a3af18ae4 --- /dev/null +++ b/examples/tailwind/astro.config.mjs @@ -0,0 +1,36 @@ +import { defineConfig } from 'astro/config'; +import starlight from '@astrojs/starlight'; +import tailwind from '@astrojs/tailwind'; + +// https://astro.build/config +export default defineConfig({ + integrations: [ + starlight({ + title: 'Docs with Tailwind', + social: { + github: 'https://github.com/withastro/starlight', + }, + sidebar: [ + { + label: 'Guides', + items: [ + // Each item here is one entry in the navigation menu. + { label: 'Example Guide', link: '/guides/example/' }, + ], + }, + { + label: 'Reference', + autogenerate: { directory: 'reference' }, + }, + ], + customCss: ['./src/tailwind.css'], + }), + tailwind({ applyBaseStyles: false }), + ], + // Process images with sharp: https://docs.astro.build/en/guides/assets/#using-sharp + image: { + service: { + entrypoint: 'astro/assets/services/sharp', + }, + }, +}); diff --git a/examples/tailwind/package.json b/examples/tailwind/package.json new file mode 100644 index 00000000000..773bd8c98eb --- /dev/null +++ b/examples/tailwind/package.json @@ -0,0 +1,21 @@ +{ + "name": "@example/starlight-tailwind", + "type": "module", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/starlight": "^0.6.1", + "@astrojs/starlight-tailwind": "^0.0.1", + "@astrojs/tailwind": "^4.0.0", + "astro": "^2.10.3", + "sharp": "^0.32.3", + "tailwindcss": "^3.3.3" + } +} diff --git a/examples/tailwind/public/favicon.svg b/examples/tailwind/public/favicon.svg new file mode 100644 index 00000000000..cba5ac140a2 --- /dev/null +++ b/examples/tailwind/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/tailwind/src/assets/houston.webp b/examples/tailwind/src/assets/houston.webp new file mode 100644 index 00000000000..930c164974a Binary files /dev/null and b/examples/tailwind/src/assets/houston.webp differ diff --git a/examples/tailwind/src/content/config.ts b/examples/tailwind/src/content/config.ts new file mode 100644 index 00000000000..9df91b60444 --- /dev/null +++ b/examples/tailwind/src/content/config.ts @@ -0,0 +1,7 @@ +import { defineCollection } from 'astro:content'; +import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; + +export const collections = { + docs: defineCollection({ schema: docsSchema() }), + i18n: defineCollection({ type: 'data', schema: i18nSchema() }), +}; diff --git a/examples/tailwind/src/content/docs/guides/example.md b/examples/tailwind/src/content/docs/guides/example.md new file mode 100644 index 00000000000..ebd0f3bc762 --- /dev/null +++ b/examples/tailwind/src/content/docs/guides/example.md @@ -0,0 +1,11 @@ +--- +title: Example Guide +description: A guide in my new Starlight docs site. +--- + +Guides lead a user through a specific task they want to accomplish, often with a sequence of steps. +Writing a good guide requires thinking about what your users are trying to do. + +## Further reading + +- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework diff --git a/examples/tailwind/src/content/docs/index.mdx b/examples/tailwind/src/content/docs/index.mdx new file mode 100644 index 00000000000..aa8d1a65791 --- /dev/null +++ b/examples/tailwind/src/content/docs/index.mdx @@ -0,0 +1,46 @@ +--- +title: Welcome to Starlight +description: Get started building your docs site with Starlight. +template: splash +hero: + title: | + Welcome to Starlight with + + Tailwind + + tagline: Congrats on setting up a new Starlight project! + image: + file: ../../assets/houston.webp + actions: + - text: Example Guide + link: /guides/example/ + icon: right-arrow + variant: primary + - text: Read the Starlight docs + link: https://starlight.astro.build + icon: external +--- + +import { Card, CardGrid } from '@astrojs/starlight/components'; + +## Next steps + + + + Edit `src/content/docs/index.mdx` to see this page change. + + + Add Markdown or MDX files to `src/content/docs` to create new pages. + + + Edit your `sidebar` and other config in `astro.config.mjs`. + + + Learn more in [the Starlight Docs](https://starlight.astro.build/). + + diff --git a/examples/tailwind/src/content/docs/reference/example.md b/examples/tailwind/src/content/docs/reference/example.md new file mode 100644 index 00000000000..ac8cfa8bc3a --- /dev/null +++ b/examples/tailwind/src/content/docs/reference/example.md @@ -0,0 +1,11 @@ +--- +title: Example Reference +description: A reference page in my new Starlight docs site. +--- + +Reference pages are ideal for outlining how things work in terse and clear terms. +Less concerned with telling a story or addressing a specific use case, they should give a comprehensive outline of what your documenting. + +## Further reading + +- Read [about reference](https://diataxis.fr/reference/) in the Diátaxis framework diff --git a/examples/tailwind/src/env.d.ts b/examples/tailwind/src/env.d.ts new file mode 100644 index 00000000000..4170bce9458 --- /dev/null +++ b/examples/tailwind/src/env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/examples/tailwind/src/tailwind.css b/examples/tailwind/src/tailwind.css new file mode 100644 index 00000000000..26664bc8816 --- /dev/null +++ b/examples/tailwind/src/tailwind.css @@ -0,0 +1,8 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* +Add additional Tailwind styles to this file, for example with @layer: +https://tailwindcss.com/docs/adding-custom-styles#using-css-and-layer +*/ diff --git a/examples/tailwind/tailwind.config.cjs b/examples/tailwind/tailwind.config.cjs new file mode 100644 index 00000000000..474874c6179 --- /dev/null +++ b/examples/tailwind/tailwind.config.cjs @@ -0,0 +1,18 @@ +const colors = require('tailwindcss/colors'); +const starlightPlugin = require('@astrojs/starlight-tailwind'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], + theme: { + extend: { + colors: { + // Your preferred accent color. Indigo is closest to Starlight’s defaults. + accent: colors.indigo, + // Your preferred gray scale. Zinc is closest to Starlight’s defaults. + gray: colors.zinc, + }, + }, + }, + plugins: [starlightPlugin()], +}; diff --git a/examples/tailwind/tsconfig.json b/examples/tailwind/tsconfig.json new file mode 100644 index 00000000000..bcbf8b50906 --- /dev/null +++ b/examples/tailwind/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "astro/tsconfigs/strict" +} diff --git a/packages/starlight/.gitignore b/packages/starlight/.gitignore index 017362793f1..fed875b1411 100644 --- a/packages/starlight/.gitignore +++ b/packages/starlight/.gitignore @@ -1,5 +1,2 @@ -# Vitest -__coverage__/ - # Astro generates this during tests, but we want to ignore it. src/env.d.ts diff --git a/packages/tailwind/README.md b/packages/tailwind/README.md new file mode 100644 index 00000000000..a9353f63052 --- /dev/null +++ b/packages/tailwind/README.md @@ -0,0 +1,18 @@ +# @astrojs/starlight-tailwind + +Tailwind CSS plugin for the [Starlight][starlight] documentation theme for [Astro][astro]. + +## Documentation + +See the [Starlight Tailwind docs][docs] for how to use this plugin. + +## License + +MIT + +Copyright (c) 2023–present [Starlight contributors][contributors] + +[starlight]: https://starlight.astro.build/ +[astro]: https://astro.build/ +[docs]: https://starlight.astro.build/guides/css-and-tailwind/#tailwind-css +[contributors]: https://github.com/withastro/starlight/graphs/contributors diff --git a/packages/tailwind/__tests__/tailwind.test.ts b/packages/tailwind/__tests__/tailwind.test.ts new file mode 100644 index 00000000000..9c2692acda6 --- /dev/null +++ b/packages/tailwind/__tests__/tailwind.test.ts @@ -0,0 +1,254 @@ +import tailwindcss, { Config } from 'tailwindcss'; +import colors from 'tailwindcss/colors'; +import postcss from 'postcss'; +import { test, expect, describe } from 'vitest'; +import StarlightTailwindPlugin from '..'; + +/** Generate a CSS string based on the passed CSS and HTML content. */ +const generatePluginCss = ({ + css = '@tailwind base;', + html = '', + config = {}, +}: { css?: string; html?: string; config?: Partial } = {}): Promise => { + return postcss( + tailwindcss({ + // Enable Starlight plugin. + plugins: [StarlightTailwindPlugin()], + // Provide content for Tailwind to scan for class names. + content: [{ raw: html, extension: 'html' }], + // Spread in any custom Tailwind config. + ...config, + }) + ) + .process(css, { from: '' }) + .then((result) => result.css); +}; + +describe('@tailwind base;', async () => { + // Generate base CSS with no core Tailwind plugins running to see just Starlight’s output. + const base = await generatePluginCss({ config: { corePlugins: [] } }); + + test('generates Starlight base CSS', async () => { + expect(base).toMatchInlineSnapshot(` + "*, ::before, ::after { + border-width: 0; + border-style: solid; + border-color: #e5e7eb; + } + ::before, ::after { + --tw-content: ; + } + :root { + --sl-font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"; + --sl-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \\"Liberation Mono\\", \\"Courier New\\", monospace; + --sl-color-white: #fff; + --sl-color-gray-1: #e5e7eb; + --sl-color-gray-2: #d1d5db; + --sl-color-gray-3: #9ca3af; + --sl-color-gray-4: #4b5563; + --sl-color-gray-5: #374151; + --sl-color-gray-6: #1f2937; + --sl-color-black: #111827; + --sl-color-accent-low: #1e1b4b; + --sl-color-accent: #4f46e5; + --sl-color-accent-high: #c7d2fe; + } + :root[data-theme=\\"light\\"] { + --sl-color-white: #111827; + --sl-color-gray-1: #1f2937; + --sl-color-gray-2: #374151; + --sl-color-gray-3: #6b7280; + --sl-color-gray-4: #9ca3af; + --sl-color-gray-5: #d1d5db; + --sl-color-gray-6: #e5e7eb; + --sl-color-gray-7: #f3f4f6; + --sl-color-black: #fff; + --sl-color-accent-low: #c7d2fe; + --sl-color-accent: #4f46e5; + --sl-color-accent-high: #312e81; + }" + `); + }); + + test('configures `--sl-color-*` variables', () => { + expect(base).includes('--sl-color-gray-1: #e5e7eb;'); + expect(base).includes('--sl-color-accent: #4f46e5;'); + }); + + describe('with user theme config', async () => { + const baseWithConfig = await generatePluginCss({ + config: { + corePlugins: [], + theme: { extend: { colors: { accent: colors.amber, gray: colors.slate } } }, + }, + }); + + test('generates different CSS from base without user config', () => { + expect(baseWithConfig).not.toEqual(base); + }); + + test('uses theme values for Starlight colours', () => { + expect(baseWithConfig).includes('--sl-color-gray-1: #e2e8f0;'); + expect(baseWithConfig).includes('--sl-color-accent: #d97706;'); + }); + }); + + test('disables Tailwind preflight', async () => { + const baseWithDefaultPlugins = await generatePluginCss(); + expect(baseWithDefaultPlugins).not.includes('line-height: 1.5;'); + expect(baseWithDefaultPlugins).includes('--tw-'); + expect(baseWithDefaultPlugins).toMatchInlineSnapshot(` + "*, ::before, ::after { + border-width: 0; + border-style: solid; + border-color: #e5e7eb; + } + ::before, ::after { + --tw-content: ; + } + :root { + --sl-font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"; + --sl-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \\"Liberation Mono\\", \\"Courier New\\", monospace; + --sl-color-white: #fff; + --sl-color-gray-1: #e5e7eb; + --sl-color-gray-2: #d1d5db; + --sl-color-gray-3: #9ca3af; + --sl-color-gray-4: #4b5563; + --sl-color-gray-5: #374151; + --sl-color-gray-6: #1f2937; + --sl-color-black: #111827; + --sl-color-accent-low: #1e1b4b; + --sl-color-accent: #4f46e5; + --sl-color-accent-high: #c7d2fe; + } + :root[data-theme=\\"light\\"] { + --sl-color-white: #111827; + --sl-color-gray-1: #1f2937; + --sl-color-gray-2: #374151; + --sl-color-gray-3: #6b7280; + --sl-color-gray-4: #9ca3af; + --sl-color-gray-5: #d1d5db; + --sl-color-gray-6: #e5e7eb; + --sl-color-gray-7: #f3f4f6; + --sl-color-black: #fff; + --sl-color-accent-low: #c7d2fe; + --sl-color-accent: #4f46e5; + --sl-color-accent-high: #312e81; + } + *, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + } + ::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + }" + `); + }); +}); + +describe('@tailwind utilities;', () => { + test('uses [data-theme="dark"] for dark: utility classes', async () => { + const utils = await generatePluginCss({ + css: '@tailwind utilities;', + html: '
', + }); + expect(utils).includes('[data-theme="dark"] .dark'); + expect(utils).toMatchInlineSnapshot(` + ":is([data-theme=\\"dark\\"] .dark\\\\:text-red-50) { + --tw-text-opacity: 1; + color: rgb(254 242 242 / var(--tw-text-opacity)) + }" + `); + }); +}); diff --git a/packages/tailwind/index.ts b/packages/tailwind/index.ts new file mode 100644 index 00000000000..103bf7dc6c2 --- /dev/null +++ b/packages/tailwind/index.ts @@ -0,0 +1,100 @@ +import plugin from 'tailwindcss/plugin'; + +/** + * Starlight Tailwind Plugin + * + * - Disables Tailwind Preflight. + * - Configures `dark:` variants for Starlight dark mode. + * - Links Starlight’s colors to `gray` and `accent` in Tailwind theme settings. + * - Links Starlight’s fonts to `sans` and `mono` in Tailwind theme settings. + * + * @example + * // tailwind.config.cjs + * const colors = require('tailwindcss/colors'); + * const starlightPlugin = require('@astrojs/starlight/tailwind'); + * + * module.exports = { + * plugins: [ + * // Add Starlight’s Tailwind plugin + * starlightPlugin(), + * ], + * theme: { + * extend: { + * colors: { + * // Set an accent color for Astro to use. Indigo is closest to Astro’s defaults. + * accent: colors.yellow, + * // Configure your preferred gray scale. Zinc is closest to Astro’s defaults. + * gray: colors.zinc, + * }, + * }, + * }, + * } + */ +const StarlightTailwindPlugin = () => + plugin( + ({ addBase, theme }) => { + /** Utility to apply accent colors based on a user’s theme config. */ + const themeAccent = ( + shade: 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950, + fallback: string + ) => + shade === 950 + ? theme(`colors.accent.${shade}`, theme(`colors.accent.900`, fallback)) + : theme(`colors.accent.${shade}`, fallback); + + addBase({ + // Restore crucial styles from Tailwind Preflight: https://tailwindcss.com/docs/preflight + // Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) + '*, ::before, ::after': { + borderWidth: '0', + borderStyle: 'solid', + borderColor: theme('borderColor.DEFAULT', 'currentColor'), + }, + '::before, ::after': { '--tw-content': '' }, + + // Wire up Starlight theme to use Tailwind config. + ':root': { + // Use Tailwind-configured font families. + '--sl-font': theme('fontFamily.sans'), + '--sl-font-mono': theme('fontFamily.mono'), + // Dark mode Starlight theme variables. + '--sl-color-white': theme('colors.white'), + '--sl-color-gray-1': theme('colors.gray.200'), + '--sl-color-gray-2': theme('colors.gray.300'), + '--sl-color-gray-3': theme('colors.gray.400'), + '--sl-color-gray-4': theme('colors.gray.600'), + '--sl-color-gray-5': theme('colors.gray.700'), + '--sl-color-gray-6': theme('colors.gray.800'), + '--sl-color-black': theme('colors.gray.900'), + '--sl-color-accent-low': themeAccent(950, '#1e1b4b'), + '--sl-color-accent': themeAccent(600, '#4f46e5'), + '--sl-color-accent-high': themeAccent(200, '#c7d2fe'), + // Light mode Starlight theme variables + '&[data-theme="light"]': { + '--sl-color-white': theme('colors.gray.900'), + '--sl-color-gray-1': theme('colors.gray.800'), + '--sl-color-gray-2': theme('colors.gray.700'), + '--sl-color-gray-3': theme('colors.gray.500'), + '--sl-color-gray-4': theme('colors.gray.400'), + '--sl-color-gray-5': theme('colors.gray.300'), + '--sl-color-gray-6': theme('colors.gray.200'), + '--sl-color-gray-7': theme('colors.gray.100'), + '--sl-color-black': theme('colors.white'), + '--sl-color-accent-low': themeAccent(200, '#c7d2fe'), + '--sl-color-accent': themeAccent(600, '#4f46e5'), + '--sl-color-accent-high': themeAccent(900, '#312e81'), + }, + }, + }); + }, + { + // Starlight uses a `data-theme` attribute to power its dark mode. + darkMode: ['class', '[data-theme="dark"]'], + corePlugins: { + // Disable Tailwind’s default reset styles which conflict with Starlight. + preflight: false, + }, + } + ); + +export default StarlightTailwindPlugin; diff --git a/packages/tailwind/package.json b/packages/tailwind/package.json new file mode 100644 index 00000000000..d06cb869fbd --- /dev/null +++ b/packages/tailwind/package.json @@ -0,0 +1,35 @@ +{ + "name": "@astrojs/starlight-tailwind", + "version": "0.0.1", + "description": "Tailwind CSS plugin for the Starlight documentation theme for Astro", + "author": "Chris Swithinbank ", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/withastro/starlight", + "directory": "packages/tailwind" + }, + "bugs": "https://github.com/withastro/starlight/issues", + "homepage": "https://starlight.astro.build", + "type": "module", + "files": [ + "index.ts" + ], + "exports": { + ".": "./index.ts" + }, + "scripts": { + "test": "vitest", + "test:coverage": "vitest run --coverage" + }, + "devDependencies": { + "@vitest/coverage-v8": "^0.33.0", + "postcss": "^8.4.27", + "vitest": "^0.33.0" + }, + "peerDependencies": { + "@astrojs/starlight": "^0.6.1", + "@astrojs/tailwind": "^4.0.0", + "tailwindcss": "^3.3.3" + } +} diff --git a/packages/tailwind/vitest.config.ts b/packages/tailwind/vitest.config.ts new file mode 100644 index 00000000000..b29e52cd64b --- /dev/null +++ b/packages/tailwind/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + coverage: { + reportsDirectory: './__coverage__', + thresholdAutoUpdate: true, + lines: 100, + functions: 100, + branches: 100, + statements: 100, + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64b6c77ca7e..05ceabf7630 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,6 +106,27 @@ importers: specifier: ^0.32.3 version: 0.32.3 + examples/tailwind: + dependencies: + '@astrojs/starlight': + specifier: ^0.6.1 + version: link:../../packages/starlight + '@astrojs/starlight-tailwind': + specifier: ^0.0.1 + version: link:../../packages/tailwind + '@astrojs/tailwind': + specifier: ^4.0.0 + version: 4.0.0(astro@2.10.4)(tailwindcss@3.3.3) + astro: + specifier: ^2.10.3 + version: 2.10.4(sharp@0.32.3) + sharp: + specifier: ^0.32.3 + version: 0.32.3 + tailwindcss: + specifier: ^3.3.3 + version: 3.3.3 + packages/starlight: dependencies: '@astrojs/mdx': @@ -167,8 +188,35 @@ importers: specifier: ^0.33.0 version: 0.33.0 + packages/tailwind: + dependencies: + '@astrojs/starlight': + specifier: ^0.6.1 + version: link:../starlight + '@astrojs/tailwind': + specifier: ^4.0.0 + version: 4.0.0(astro@2.10.4)(tailwindcss@3.3.3) + tailwindcss: + specifier: ^3.3.3 + version: 3.3.3 + devDependencies: + '@vitest/coverage-v8': + specifier: ^0.33.0 + version: 0.33.0(vitest@0.33.0) + postcss: + specifier: ^8.4.27 + version: 8.4.27 + vitest: + specifier: ^0.33.0 + version: 0.33.0 + packages: + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + dev: false + /@ampproject/remapping@2.2.1: resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} engines: {node: '>=6.0.0'} @@ -262,6 +310,21 @@ packages: zod: 3.21.4 dev: false + /@astrojs/tailwind@4.0.0(astro@2.10.4)(tailwindcss@3.3.3): + resolution: {integrity: sha512-HmCAXFFes7MUBt5ihdfH1goa8QyGkHejIpz6Z4XBKK9VNYY9G2E3brCn8+pNn5zAOzcwl3FYcuH2AiOa/NGoMQ==} + peerDependencies: + astro: ^2.6.5 + tailwindcss: ^3.0.24 + dependencies: + astro: 2.10.4(sharp@0.32.3) + autoprefixer: 10.4.14(postcss@8.4.27) + postcss: 8.4.27 + postcss-load-config: 4.0.1(postcss@8.4.27) + tailwindcss: 3.3.3 + transitivePeerDependencies: + - ts-node + dev: false + /@astrojs/telemetry@2.1.1: resolution: {integrity: sha512-4pRhyeQr0MLB5PKYgkdu+YE8sSpMbHL8dUuslBWBIdgcYjtD1SufPMBI8pgXJ+xlwrQJHKKfK2X1KonHYuOS9A==} engines: {node: '>=16.12.0'} @@ -1587,6 +1650,10 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: false + /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -1824,6 +1891,22 @@ packages: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: true + /autoprefixer@10.4.14(postcss@8.4.27): + resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.5 + caniuse-lite: 1.0.30001466 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.27 + postcss-value-parser: 4.2.0 + dev: false + /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -1851,7 +1934,6 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -1928,7 +2010,6 @@ packages: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} @@ -1991,6 +2072,11 @@ packages: get-intrinsic: 1.2.0 dev: true + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: false + /camelcase-keys@6.2.2: resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} engines: {node: '>=8'} @@ -2197,6 +2283,11 @@ packages: /comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: false + /commander@6.2.1: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} engines: {node: '>= 6'} @@ -2212,7 +2303,6 @@ packages: /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true /convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -2256,6 +2346,12 @@ packages: engines: {node: '>= 6'} dev: true + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: false + /csv-generate@3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} dev: true @@ -2383,6 +2479,10 @@ packages: resolution: {integrity: sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==} dev: true + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: false + /diff-sequences@29.4.3: resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3096,6 +3196,10 @@ packages: fetch-blob: 3.2.0 dev: true + /fraction.js@4.2.0: + resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + dev: false + /from@0.1.7: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} dev: true @@ -3123,7 +3227,6 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} @@ -3204,6 +3307,24 @@ packages: dependencies: is-glob: 4.0.3 + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: false + + /glob@7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: false + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -3549,7 +3670,6 @@ packages: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -3637,6 +3757,12 @@ packages: dependencies: has: 1.0.3 + /is-core-module@2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + dependencies: + has: 1.0.3 + dev: false + /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} @@ -3831,6 +3957,11 @@ packages: istanbul-lib-report: 3.0.0 dev: true + /jiti@1.19.1: + resolution: {integrity: sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==} + hasBin: true + dev: false + /joi@17.9.2: resolution: {integrity: sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==} dependencies: @@ -3903,11 +4034,9 @@ packages: /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} - dev: true /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: true /load-yaml-file@0.2.0: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} @@ -4601,7 +4730,6 @@ packages: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 - dev: true /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} @@ -4644,6 +4772,14 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: false + /nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -4736,6 +4872,11 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: false + /not@0.1.0: resolution: {integrity: sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==} dev: false @@ -4761,7 +4902,11 @@ packages: /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - dev: true + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: false /object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} @@ -4999,7 +5144,6 @@ packages: /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} - dev: true /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} @@ -5056,7 +5200,6 @@ packages: /pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - dev: true /pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} @@ -5074,6 +5217,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: false + /pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} @@ -5088,6 +5236,67 @@ packages: pathe: 1.1.1 dev: true + /postcss-import@15.1.0(postcss@8.4.27): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.27 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.4 + dev: false + + /postcss-js@4.0.1(postcss@8.4.27): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.27 + dev: false + + /postcss-load-config@4.0.1(postcss@8.4.27): + resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.4.27 + yaml: 2.3.1 + dev: false + + /postcss-nested@6.0.1(postcss@8.4.27): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.27 + postcss-selector-parser: 6.0.13 + dev: false + + /postcss-selector-parser@6.0.13: + resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: false + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: false + /postcss@8.4.27: resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==} engines: {node: ^10 || ^12 || >=14} @@ -5264,6 +5473,12 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: false + /read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -5444,6 +5659,15 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + /resolve@1.22.4: + resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} + hasBin: true + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: false + /restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5932,6 +6156,20 @@ packages: inline-style-parser: 0.1.1 dev: false + /sucrase@3.34.0: + resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} + engines: {node: '>=8'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.2 + commander: 4.1.1 + glob: 7.1.6 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: false + /suf-log@2.5.3: resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==} dependencies: @@ -5960,6 +6198,37 @@ packages: '@pkgr/utils': 2.3.1 tslib: 2.5.0 + /tailwindcss@3.3.3: + resolution: {integrity: sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.2.12 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.19.1 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.27 + postcss-import: 15.1.0(postcss@8.4.27) + postcss-js: 4.0.1(postcss@8.4.27) + postcss-load-config: 4.0.1(postcss@8.4.27) + postcss-nested: 6.0.1(postcss@8.4.27) + postcss-selector-parser: 6.0.13 + resolve: 1.22.4 + sucrase: 3.34.0 + transitivePeerDependencies: + - ts-node + dev: false + /tar-fs@2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} dependencies: @@ -6006,6 +6275,19 @@ packages: minimatch: 3.1.2 dev: true + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: false + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: false + /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} dev: true @@ -6066,6 +6348,10 @@ packages: resolution: {integrity: sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==} dev: true + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: false + /tsconfig-resolver@3.0.1: resolution: {integrity: sha512-ZHqlstlQF449v8glscGRXzL6l2dZvASPCdXJRWG4gHEZlUVx2Jtmr+a2zeVG4LCsKhDXKRj5R3h0C/98UcVAQg==} dependencies: @@ -6660,6 +6946,11 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + /yaml@2.3.1: + resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} + engines: {node: '>= 14'} + dev: false + /yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'}