Compare commits
1 commit
main
...
reference-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e00c01a1d |
4 changed files with 441 additions and 0 deletions
256
packages/tailwindcss/reference.css
Normal file
256
packages/tailwindcss/reference.css
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
/* Tailwind CSS syntax reference: demonstrates all supported directives and functions */
|
||||
|
||||
@config "./reference/reference.config.js";
|
||||
|
||||
@plugin "./reference/reference.plugin.js" {
|
||||
enable-grid: true;
|
||||
card-color: 'white';
|
||||
accent-scale: 'sky', 'amber';
|
||||
shadow-depth: 24;
|
||||
}
|
||||
|
||||
@import 'tailwindcss' prefix(tw);
|
||||
@import 'tailwindcss/utilities' important;
|
||||
@import 'tailwindcss/theme' theme(static);
|
||||
@import './reference/reference-import.css' layer(utilities) supports(display: grid);
|
||||
|
||||
@reference 'tailwindcss/theme';
|
||||
@reference './reference/reference-variant.css';
|
||||
|
||||
@source "./app/**/*.{js,ts,jsx,tsx}";
|
||||
@source not "./app/**/__tests__/*";
|
||||
@source inline("tw:prose tw:underline");
|
||||
@source not inline("tw:legacy");
|
||||
|
||||
@theme reference prefix(tw);
|
||||
|
||||
@theme default {
|
||||
--color-brand: #1d4ed8;
|
||||
--font-sans: 'Inter', system-ui, sans-serif;
|
||||
--spacing: 0.25rem;
|
||||
|
||||
@keyframes reference-pulse {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@theme {
|
||||
--color-accent: oklch(70% 0.12 220);
|
||||
--radius-card: 1rem;
|
||||
--value-sm: 14px;
|
||||
--modifier-7: 28px;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--shadow-card: 0 25px 50px -12px color-mix(in oklab, var(--color-brand) 25%, transparent);
|
||||
}
|
||||
|
||||
@theme inline prefix(tw) {
|
||||
--size-icon: 1.25rem;
|
||||
}
|
||||
|
||||
@theme reference inline {
|
||||
--card-outline: 1px solid color-mix(in oklab, var(--color-brand) 60%, transparent);
|
||||
}
|
||||
|
||||
@theme static {
|
||||
--ring-brand: var(--color-brand);
|
||||
|
||||
@keyframes reference-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media theme(reference) prefix(tw) important {
|
||||
@theme {
|
||||
--color-card-foreground: var(--color-accent);
|
||||
}
|
||||
}
|
||||
|
||||
@media source("./components/**/*.{tsx,jsx}") theme(inline) {
|
||||
@theme {
|
||||
--spacing-component: calc(var(--spacing) * 6);
|
||||
}
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities source("./app/**/*.{html,js,ts,jsx,tsx}");
|
||||
|
||||
@layer base {
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: theme('fontFamily.sans', var(--font-sans));
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: --alpha(var(--color-brand) / 7.5%);
|
||||
color: var(--color-card-foreground, var(--color-accent));
|
||||
}
|
||||
}
|
||||
|
||||
@layer theme {
|
||||
@theme {
|
||||
--layer-spacing-8: calc(var(--spacing) * 8);
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.card {
|
||||
@apply tw:bg-white tw:text-slate-900 tw:shadow-lg tw:rounded;
|
||||
padding: --spacing(6);
|
||||
border-radius: --theme(--radius-card, 0.75rem);
|
||||
outline: --theme(--card-outline inline, 0 solid transparent);
|
||||
box-shadow: var(--shadow-card, --theme(--shadow-card, 0 10px 30px rgba(15, 23, 42, 0.15)));
|
||||
}
|
||||
|
||||
.card {
|
||||
@variant reference-dark {
|
||||
outline-color: --theme(--ring-brand inline, var(--color-brand));
|
||||
}
|
||||
|
||||
@variant motion-safe {
|
||||
transition: transform 200ms ease;
|
||||
}
|
||||
|
||||
@variant hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: --spacing(2);
|
||||
font-size: theme('fontSize.sm', 0.875rem);
|
||||
}
|
||||
|
||||
.chip {
|
||||
@variant hocus {
|
||||
@apply tw:ring-2 tw:ring-offset-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
@variant hover {
|
||||
.hover\\:reference-underline {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@utility card {
|
||||
--tw-card-shadow: var(--shadow-card);
|
||||
box-shadow: var(--tw-card-shadow);
|
||||
}
|
||||
|
||||
@utility stack-* {
|
||||
--stack-gap: calc(var(--spacing) * --value(number));
|
||||
margin-top: var(--stack-gap);
|
||||
margin-bottom: var(--stack-gap);
|
||||
}
|
||||
|
||||
@utility aspect-* {
|
||||
aspect-ratio: --value(ratio);
|
||||
}
|
||||
|
||||
@utility example-* {
|
||||
--value: --value(--value, [length]);
|
||||
--modifier: --modifier(--modifier, [length]);
|
||||
--modifier-with-calc: calc(--modifier(--modifier, [length]) * 2);
|
||||
--modifier-literals: --modifier('literal', 'literal-2');
|
||||
}
|
||||
|
||||
@utility mask-r-* {
|
||||
--mask-right: linear-gradient(
|
||||
to left,
|
||||
transparent calc(var(--spacing) * --modifier(integer)),
|
||||
black calc(var(--spacing) * --value(integer))
|
||||
);
|
||||
mask-image: var(--mask-right);
|
||||
}
|
||||
|
||||
@custom-variant hocus (&:hover, &:focus);
|
||||
|
||||
@custom-variant theme-wrap {
|
||||
@variant reference-dark {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@variant reference-dark {
|
||||
&:where([data-theme='reference-dark'] *) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@variant motion-safe {
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@variant hover {
|
||||
&:hover {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@variant hover {
|
||||
@variant reference-dark {
|
||||
.reference-hover-dark {
|
||||
color: var(--color-brand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@variant reference-dark {
|
||||
@page {
|
||||
margin: 1in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
border-radius: var(--radius-card);
|
||||
padding-inline: --spacing(3);
|
||||
padding-block: --spacing(1.5);
|
||||
}
|
||||
|
||||
.badge {
|
||||
@variant reference-dark {
|
||||
border-color: var(--color-brand);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: var(--size-icon);
|
||||
height: var(--size-icon);
|
||||
}
|
||||
|
||||
.icon {
|
||||
@variant theme-wrap {
|
||||
color: var(--color-card-foreground, var(--color-brand));
|
||||
}
|
||||
}
|
||||
12
packages/tailwindcss/reference/reference-import.css
vendored
Normal file
12
packages/tailwindcss/reference/reference-import.css
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/* Imported stylesheet used by reference.css via @import */
|
||||
|
||||
@theme inline {
|
||||
--imported-gap: 1.5rem;
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.from-import {
|
||||
display: grid;
|
||||
gap: var(--imported-gap, 1rem);
|
||||
}
|
||||
}
|
||||
19
packages/tailwindcss/reference/reference-variant.css
Normal file
19
packages/tailwindcss/reference/reference-variant.css
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* Referenced stylesheet providing additional variants and theme tokens */
|
||||
|
||||
@theme reference {
|
||||
--reference-accent: #f97316;
|
||||
}
|
||||
|
||||
@variant data-open {
|
||||
&[data-open='true'] {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@variant data-open {
|
||||
@variant hover {
|
||||
.data-open-hover {
|
||||
color: var(--reference-accent, currentColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -217,6 +217,160 @@ describe('compiling CSS', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
test('reference.css compiles end-to-end', async () => {
|
||||
let referencePath = path.resolve(__dirname, '..', 'reference.css')
|
||||
let referenceCss = fs.readFileSync(referencePath, 'utf-8')
|
||||
let referenceDir = path.dirname(referencePath)
|
||||
|
||||
let builtinStyles = new Map<string, string>([
|
||||
['tailwindcss', path.resolve(referenceDir, 'index.css')],
|
||||
['tailwindcss/index.css', path.resolve(referenceDir, 'index.css')],
|
||||
['tailwindcss/preflight', path.resolve(referenceDir, 'preflight.css')],
|
||||
['tailwindcss/preflight.css', path.resolve(referenceDir, 'preflight.css')],
|
||||
['tailwindcss/theme', path.resolve(referenceDir, 'theme.css')],
|
||||
['tailwindcss/theme.css', path.resolve(referenceDir, 'theme.css')],
|
||||
['tailwindcss/utilities', path.resolve(referenceDir, 'utilities.css')],
|
||||
['tailwindcss/utilities.css', path.resolve(referenceDir, 'utilities.css')],
|
||||
])
|
||||
|
||||
let loadStylesheet = async (id: string, base: string) => {
|
||||
let normalizedBase = base === '' ? referenceDir : base
|
||||
let normalizedId = id.replace(/\\/g, '/')
|
||||
if (builtinStyles.has(normalizedId)) {
|
||||
let filePath = builtinStyles.get(normalizedId)!
|
||||
return {
|
||||
path: filePath,
|
||||
base: path.dirname(filePath),
|
||||
content: fs.readFileSync(filePath, 'utf-8'),
|
||||
}
|
||||
}
|
||||
|
||||
let resolved = path.resolve(normalizedBase, id)
|
||||
return {
|
||||
path: resolved,
|
||||
base: path.dirname(resolved),
|
||||
content: fs.readFileSync(resolved, 'utf-8'),
|
||||
}
|
||||
}
|
||||
|
||||
let referencePlugin = plugin.withOptions(
|
||||
(options: Record<string, unknown> = {}) => {
|
||||
return ({ addUtilities, addVariant, theme }: PluginAPI) => {
|
||||
let enableGrid =
|
||||
typeof options['enable-grid'] === 'boolean' ? options['enable-grid'] : true
|
||||
let cardColor =
|
||||
typeof options['card-color'] === 'string' ? options['card-color'] : 'white'
|
||||
let shadowDepthValue = options['shadow-depth']
|
||||
let shadowDepth =
|
||||
typeof shadowDepthValue === 'number'
|
||||
? shadowDepthValue
|
||||
: Number(shadowDepthValue) || 16
|
||||
|
||||
let accentScale = Array.isArray(options['accent-scale']) ? options['accent-scale'] : []
|
||||
let accentUtilities: Record<string, Record<string, string>> = {}
|
||||
for (let accent of accentScale) {
|
||||
if (typeof accent === 'string') {
|
||||
accentUtilities[`.plugin-accent-${accent}`] = {
|
||||
color: `var(--color-${accent}, currentColor)`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addUtilities({
|
||||
'.plugin-card': {
|
||||
display: enableGrid ? 'grid' : 'block',
|
||||
gap: theme('spacing.6', '1.5rem'),
|
||||
'background-color': cardColor,
|
||||
'box-shadow': `0 ${shadowDepth}px 32px -12px rgba(15, 23, 42, 0.18)`,
|
||||
},
|
||||
...accentUtilities,
|
||||
})
|
||||
|
||||
addVariant('supports-grid', {
|
||||
'@supports (display: grid)': '@slot',
|
||||
})
|
||||
}
|
||||
},
|
||||
() => ({
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: '#2563eb',
|
||||
'card-foreground': '#1f2937',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
let referenceConfig = {
|
||||
darkMode: ['class', '[data-theme="reference-dark"]'],
|
||||
safelist: ['tw:underline', 'plugin-card'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: '#1d4ed8',
|
||||
'card-foreground': '#111827',
|
||||
},
|
||||
spacing: {
|
||||
6: '1.5rem',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
let moduleRegistry = new Map<string, any>([
|
||||
[path.resolve(referenceDir, 'reference/reference.plugin.js'), referencePlugin],
|
||||
[path.resolve(referenceDir, 'reference/reference.config.js'), referenceConfig],
|
||||
])
|
||||
|
||||
let loadModule = async (id: string, base: string) => {
|
||||
let normalizedBase = base === '' ? referenceDir : base
|
||||
let resolved = path.resolve(normalizedBase, id)
|
||||
if (!moduleRegistry.has(resolved)) {
|
||||
throw new Error(`Unknown module requested by reference.css test: ${id}`)
|
||||
}
|
||||
|
||||
return {
|
||||
path: resolved,
|
||||
base: path.dirname(resolved),
|
||||
module: moduleRegistry.get(resolved),
|
||||
}
|
||||
}
|
||||
|
||||
let candidates = [
|
||||
'tw:underline',
|
||||
'stack-4',
|
||||
'aspect-16/9',
|
||||
'example-sm/7',
|
||||
'example-[12px]/[16px]',
|
||||
'mask-r-4/6',
|
||||
'theme-wrap:tw:bg-brand',
|
||||
'supports-grid:tw:flex',
|
||||
'motion-safe:tw:animate-pulse',
|
||||
'reference-dark:tw:text-brand',
|
||||
'data-open:tw:px-4',
|
||||
'hocus:tw:ring-2',
|
||||
'plugin-card',
|
||||
]
|
||||
|
||||
let output = await compileCss(referenceCss, candidates, {
|
||||
from: referencePath,
|
||||
loadStylesheet,
|
||||
loadModule,
|
||||
})
|
||||
|
||||
expect(output).toContain('.stack-4')
|
||||
expect(output).toContain('.example-sm\\/7')
|
||||
expect(output).toContain('.mask-r-4\\/6')
|
||||
expect(output).toContain('.supports-grid\\:tw\\:flex')
|
||||
expect(output).toContain('.theme-wrap\\:tw\\:bg-brand')
|
||||
expect(output).toContain('.plugin-card')
|
||||
expect(output).toContain('.data-open\\:tw\\:px-4')
|
||||
expect(output).toContain('.from-import')
|
||||
expect(output).toMatch(/@media \\(prefers-reduced-motion: no-preference\\)/)
|
||||
})
|
||||
|
||||
test('adds vendor prefixes', async () => {
|
||||
expect(
|
||||
await compileCss(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue