Search
Search
Search
Search
Information
Information
Light
Dark
Open actions menu
Basic upload method
Bypass upload method
Tips!
If you encounter an error (by firewall) while uploading using both methods,
try changing extension of the file before uploading it and rename it right after.
This uploader supports multiple file upload.
Submit
~
var
www
gtechmarathon2026.bitkit.dk
httpdocs
node_modules
@tailwindcss
typography
src
File Content:
index.test.js
const path = require('path') const tailwind = require('tailwindcss') const postcss = require('postcss') const typographyPlugin = require('.') let html = String.raw let css = String.raw let vars = ` --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-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: ; ` let defaults = css` *, ::before, ::after { ${vars} } ::backdrop { ${vars} } ` function run(config, plugin = tailwind) { let { currentTestName } = expect.getState() config = { ...{ plugins: [typographyPlugin], corePlugins: { preflight: false } }, ...config, } return postcss(plugin(config)).process( ['@tailwind base;', '@tailwind components;', '@tailwind utilities'].join('\n'), { from: `${path.resolve(__filename)}?test=${currentTestName}`, } ) } test('specificity is reduced with :where', async () => { let config = { content: [{ raw: html`<div class="prose"></div>` }], theme: { typography: { DEFAULT: { css: [ { color: 'var(--tw-prose-body)', maxWidth: '65ch', '[class~="lead"]': { color: 'var(--tw-prose-lead)', }, strong: { color: 'var(--tw-prose-bold)', fontWeight: '600', }, 'ol[type="A"]': { listStyleType: 'upper-alpha', }, 'blockquote p:first-of-type::before': { content: 'open-quote', }, 'blockquote p:last-of-type::after': { content: 'close-quote', }, 'h4 strong': { fontWeight: '700', }, 'figure > *': { margin: 0, }, 'ol > li::marker': { fontWeight: '400', color: 'var(--tw-prose-counters)', }, '> ul > li p': { marginTop: '16px', marginBottom: '16px', }, 'code::before': { content: '"`"', }, 'code::after': { content: '"`"', }, }, ], }, }, }, } return run(config).then((result) => { expect(result.css).toMatchFormattedCss( css` ${defaults} .prose { color: var(--tw-prose-body); max-width: 65ch; } .prose :where([class~='lead']):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: var(--tw-prose-lead); } .prose :where(strong):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: var(--tw-prose-bold); font-weight: 600; } .prose :where(ol[type='A']):not(:where([class~='not-prose'], [class~='not-prose'] *)) { list-style-type: upper-alpha; } .prose :where(blockquote p:first-of-type):not(:where([class~='not-prose'], [class~='not-prose'] *))::before { content: open-quote; } .prose :where(blockquote p:last-of-type):not(:where([class~='not-prose'], [class~='not-prose'] *))::after { content: close-quote; } .prose :where(h4 strong):not(:where([class~='not-prose'], [class~='not-prose'] *)) { font-weight: 700; } .prose :where(figure > *):not(:where([class~='not-prose'], [class~='not-prose'] *)) { margin: 0; } .prose :where(ol > li):not(:where([class~='not-prose'], [class~='not-prose'] *))::marker { font-weight: 400; color: var(--tw-prose-counters); } .prose :where(.prose > ul > li p):not(:where([class~='not-prose'], [class~='not-prose'] *)) { margin-top: 16px; margin-bottom: 16px; } .prose :where(code):not(:where([class~='not-prose'], [class~='not-prose'] *))::before { content: '`'; } .prose :where(code):not(:where([class~='not-prose'], [class~='not-prose'] *))::after { content: '`'; } ` ) }) }) test('variants', async () => { let config = { content: [{ raw: html`<div class="sm:prose hover:prose-lg lg:prose-lg"></div>` }], theme: { typography: { DEFAULT: { css: [ { color: 'red', p: { color: 'lime', }, '> ul > li': { color: 'purple', }, }, ], }, lg: { css: { color: 'green', p: { color: 'tomato', }, '> ul > li': { color: 'blue', }, }, }, xl: { css: { color: 'yellow', '> ul > li': { color: 'hotpink', }, }, }, }, }, } return run(config).then((result) => { expect(result.css).toMatchFormattedCss( css` ${defaults} .hover\:prose-lg:hover { color: green; } .hover\:prose-lg:hover :where(p):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: tomato; } .hover\:prose-lg:hover :where(.hover\:prose-lg:hover > ul > li):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: blue; } @media (min-width: 640px) { .sm\:prose { color: red; } .sm\:prose :where(p):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: lime; } .sm\:prose :where(.sm\:prose > ul > li):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: purple; } } @media (min-width: 1024px) { .lg\:prose-lg { color: green; } .lg\:prose-lg :where(p):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: tomato; } .lg\:prose-lg :where(.lg\:prose-lg > ul > li):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: blue; } } ` ) }) }) test('modifiers', async () => { let config = { content: [{ raw: html`<div class="prose prose-lg"></div>` }], theme: { typography: { DEFAULT: { css: [ { color: 'var(--tw-prose-body)', maxWidth: '65ch', '[class~="lead"]': { color: 'var(--tw-prose-lead)', }, strong: { color: 'var(--tw-prose-bold)', fontWeight: '600', }, 'ol[type="A"]': { listStyleType: 'upper-alpha', }, 'blockquote p:first-of-type::before': { content: 'open-quote', }, 'blockquote p:last-of-type::after': { content: 'close-quote', }, 'h4 strong': { fontWeight: '700', }, 'figure > *': { margin: 0, }, 'ol > li::marker': { fontWeight: '400', color: 'var(--tw-prose-counters)', }, 'code::before': { content: '"`"', }, 'code::after': { content: '"`"', }, }, ], }, lg: { css: [ { fontSize: '18px', lineHeight: '1.75', p: { marginTop: '24px', marginBottom: '24px', }, '[class~="lead"]': { fontSize: '22px', }, blockquote: { marginTop: '40px', marginBottom: '40px', }, '> ul > li': { paddingLeft: '12px', }, h1: { fontSize: '48px', marginTop: '0', marginBottom: '40px', }, h2: { fontSize: '30px', marginTop: '56px', marginBottom: '32px', }, h3: { fontSize: '24px', marginTop: '40px', marginBottom: '16px', }, }, ], }, }, }, } return run(config).then((result) => { expect(result.css).toMatchFormattedCss( css` ${defaults} .prose { color: var(--tw-prose-body); max-width: 65ch; } .prose :where([class~='lead']):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: var(--tw-prose-lead); } .prose :where(strong):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: var(--tw-prose-bold); font-weight: 600; } .prose :where(ol[type='A']):not(:where([class~='not-prose'], [class~='not-prose'] *)) { list-style-type: upper-alpha; } .prose :where(blockquote p:first-of-type):not(:where([class~='not-prose'], [class~='not-prose'] *))::before { content: open-quote; } .prose :where(blockquote p:last-of-type):not(:where([class~='not-prose'], [class~='not-prose'] *))::after { content: close-quote; } .prose :where(h4 strong):not(:where([class~='not-prose'], [class~='not-prose'] *)) { font-weight: 700; } .prose :where(figure > *):not(:where([class~='not-prose'], [class~='not-prose'] *)) { margin: 0; } .prose :where(ol > li):not(:where([class~='not-prose'], [class~='not-prose'] *))::marker { font-weight: 400; color: var(--tw-prose-counters); } .prose :where(code):not(:where([class~='not-prose'], [class~='not-prose'] *))::before { content: '`'; } .prose :where(code):not(:where([class~='not-prose'], [class~='not-prose'] *))::after { content: '`'; } .prose-lg { font-size: 18px; line-height: 1.75; } .prose-lg :where(p):not(:where([class~='not-prose'], [class~='not-prose'] *)) { margin-top: 24px; margin-bottom: 24px; } .prose-lg :where([class~='lead']):not(:where([class~='not-prose'], [class~='not-prose'] *)) { font-size: 22px; } .prose-lg :where(blockquote):not(:where([class~='not-prose'], [class~='not-prose'] *)) { margin-top: 40px; margin-bottom: 40px; } .prose-lg :where(.prose-lg > ul > li):not(:where([class~='not-prose'], [class~='not-prose'] *)) { padding-left: 12px; } .prose-lg :where(h1):not(:where([class~='not-prose'], [class~='not-prose'] *)) { font-size: 48px; margin-top: 0; margin-bottom: 40px; } .prose-lg :where(h2):not(:where([class~='not-prose'], [class~='not-prose'] *)) { font-size: 30px; margin-top: 56px; margin-bottom: 32px; } .prose-lg :where(h3):not(:where([class~='not-prose'], [class~='not-prose'] *)) { font-size: 24px; margin-top: 40px; margin-bottom: 16px; } ` ) }) }) test('legacy target', async () => { let config = { plugins: [typographyPlugin({ target: 'legacy' })], content: [ { raw: html`<div class="prose prose-h1:text-center prose-headings:text-ellipsis"></div>` }, ], theme: { typography: { DEFAULT: { css: [ { color: 'var(--tw-prose-body)', maxWidth: '65ch', '[class~="lead"]': { color: 'var(--tw-prose-lead)', }, strong: { color: 'var(--tw-prose-bold)', fontWeight: '600', }, 'ol[type="A"]': { listStyleType: 'upper-alpha', }, 'blockquote p:first-of-type::before': { content: 'open-quote', }, 'blockquote p:last-of-type::after': { content: 'close-quote', }, 'h4 strong': { fontWeight: '700', }, 'figure > *': { margin: 0, }, 'ol > li::marker': { fontWeight: '400', color: 'var(--tw-prose-counters)', }, 'code::before': { content: '"`"', }, 'code::after': { content: '"`"', }, }, ], }, }, }, } return run(config).then((result) => { expect(result.css).toMatchFormattedCss( css` ${defaults} .prose { color: var(--tw-prose-body); max-width: 65ch; } .prose [class~='lead'] { color: var(--tw-prose-lead); } .prose strong { color: var(--tw-prose-bold); font-weight: 600; } .prose ol[type='A'] { list-style-type: upper-alpha; } .prose blockquote p:first-of-type::before { content: open-quote; } .prose blockquote p:last-of-type::after { content: close-quote; } .prose h4 strong { font-weight: 700; } .prose figure > * { margin: 0; } .prose ol > li::marker { font-weight: 400; color: var(--tw-prose-counters); } .prose code::before { content: '`'; } .prose code::after { content: '`'; } .prose-headings\:text-ellipsis h1 { text-overflow: ellipsis; } .prose-headings\:text-ellipsis h2 { text-overflow: ellipsis; } .prose-headings\:text-ellipsis h3 { text-overflow: ellipsis; } .prose-headings\:text-ellipsis h4 { text-overflow: ellipsis; } .prose-headings\:text-ellipsis h5 { text-overflow: ellipsis; } .prose-headings\:text-ellipsis h6 { text-overflow: ellipsis; } .prose-headings\:text-ellipsis th { text-overflow: ellipsis; } .prose-h1\:text-center h1 { text-align: center; } ` ) }) }) test('custom class name', async () => { let config = { plugins: [typographyPlugin({ className: 'markdown' })], content: [{ raw: html`<div class="markdown"></div>` }], theme: { typography: { DEFAULT: { css: [ { color: 'var(--tw-prose-body)', maxWidth: '65ch', '[class~="lead"]': { color: 'var(--tw-prose-lead)', }, strong: { color: 'var(--tw-prose-bold)', fontWeight: '600', }, 'ol[type="A"]': { listStyleType: 'upper-alpha', }, 'blockquote p:first-of-type::before': { content: 'open-quote', }, 'blockquote p:last-of-type::after': { content: 'close-quote', }, 'h4 strong': { fontWeight: '700', }, 'figure > *': { margin: 0, }, 'ol > li::marker': { fontWeight: '400', color: 'var(--tw-prose-counters)', }, 'code::before': { content: '"`"', }, 'code::after': { content: '"`"', }, }, ], }, }, }, } return run(config).then((result) => { expect(result.css).toMatchFormattedCss( css` ${defaults} .markdown { color: var(--tw-prose-body); max-width: 65ch; } .markdown :where([class~='lead']):not(:where([class~='not-markdown'], [class~='not-markdown'] *)) { color: var(--tw-prose-lead); } .markdown :where(strong):not(:where([class~='not-markdown'], [class~='not-markdown'] *)) { color: var(--tw-prose-bold); font-weight: 600; } .markdown :where(ol[type='A']):not(:where([class~='not-markdown'], [class~='not-markdown'] *)) { list-style-type: upper-alpha; } .markdown :where(blockquote p:first-of-type):not(:where([class~='not-markdown'], [class~='not-markdown'] *))::before { content: open-quote; } .markdown :where(blockquote p:last-of-type):not(:where([class~='not-markdown'], [class~='not-markdown'] *))::after { content: close-quote; } .markdown :where(h4 strong):not(:where([class~='not-markdown'], [class~='not-markdown'] *)) { font-weight: 700; } .markdown :where(figure > *):not(:where([class~='not-markdown'], [class~='not-markdown'] *)) { margin: 0; } .markdown :where(ol > li):not(:where([class~='not-markdown'], [class~='not-markdown'] *))::marker { font-weight: 400; color: var(--tw-prose-counters); } .markdown :where(code):not(:where([class~='not-markdown'], [class~='not-markdown'] *))::before { content: '`'; } .markdown :where(code):not(:where([class~='not-markdown'], [class~='not-markdown'] *))::after { content: '`'; } ` ) }) }) test('element variants', async () => { let config = { content: [ { raw: html`<div class=" prose prose-headings:underline prose-lead:italic prose-h1:text-3xl prose-h2:text-2xl prose-h3:text-xl prose-h4:text-lg prose-p:text-gray-700 prose-a:font-bold prose-blockquote:italic prose-figure:mx-auto prose-figcaption:opacity-75 prose-strong:font-medium prose-em:italic prose-kbd:border-b-2 prose-code:font-mono prose-pre:font-mono prose-ol:pl-6 prose-ul:pl-8 prose-li:my-4 prose-table:my-8 prose-thead:border-red-300 prose-tr:border-red-200 prose-th:text-left prose-td:align-center prose-img:rounded-lg prose-video:my-12 prose-hr:border-t-2 " ></div>`, }, ], theme: { typography: { DEFAULT: { css: [ { color: 'var(--tw-prose-body)', '[class~="lead"]': { color: 'var(--tw-prose-lead)', }, strong: { color: 'var(--tw-prose-bold)', fontWeight: '600', }, 'h4 strong': { fontWeight: '700', }, }, ], }, }, }, } return run(config).then((result) => { expect(result.css).toMatchFormattedCss( css` ${defaults} .prose { color: var(--tw-prose-body); } .prose :where([class~='lead']):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: var(--tw-prose-lead); } .prose :where(strong):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: var(--tw-prose-bold); font-weight: 600; } .prose :where(h4 strong):not(:where([class~='not-prose'], [class~='not-prose'] *)) { font-weight: 700; } .prose-headings\:underline :is(:where(h1, h2, h3, h4, h5, h6, th):not(:where([class~='not-prose'], [class~='not-prose'] *))) { text-decoration-line: underline; } .prose-h1\:text-3xl :is(:where(h1):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-size: 1.875rem; line-height: 2.25rem; } .prose-h2\:text-2xl :is(:where(h2):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-size: 1.5rem; line-height: 2rem; } .prose-h3\:text-xl :is(:where(h3):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-size: 1.25rem; line-height: 1.75rem; } .prose-h4\:text-lg :is(:where(h4):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-size: 1.125rem; line-height: 1.75rem; } .prose-p\:text-gray-700 :is(:where(p):not(:where([class~='not-prose'], [class~='not-prose'] *))) { --tw-text-opacity: 1; color: rgb(55 65 81 / var(--tw-text-opacity)); } .prose-a\:font-bold :is(:where(a):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-weight: 700; } .prose-blockquote\:italic :is(:where(blockquote):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-style: italic; } .prose-figure\:mx-auto :is(:where(figure):not(:where([class~='not-prose'], [class~='not-prose'] *))) { margin-left: auto; margin-right: auto; } .prose-figcaption\:opacity-75 :is(:where(figcaption):not(:where([class~='not-prose'], [class~='not-prose'] *))) { opacity: 0.75; } .prose-strong\:font-medium :is(:where(strong):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-weight: 500; } .prose-em\:italic :is(:where(em):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-style: italic; } .prose-kbd\:border-b-2 :is(:where(kbd):not(:where([class~='not-prose'], [class~='not-prose'] *))) { border-bottom-width: 2px; } .prose-code\:font-mono :is(:where(code):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; } .prose-pre\:font-mono :is(:where(pre):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; } .prose-ol\:pl-6 :is(:where(ol):not(:where([class~='not-prose'], [class~='not-prose'] *))) { padding-left: 1.5rem; } .prose-ul\:pl-8 :is(:where(ul):not(:where([class~='not-prose'], [class~='not-prose'] *))) { padding-left: 2rem; } .prose-li\:my-4 :is(:where(li):not(:where([class~='not-prose'], [class~='not-prose'] *))) { margin-top: 1rem; margin-bottom: 1rem; } .prose-table\:my-8 :is(:where(table):not(:where([class~='not-prose'], [class~='not-prose'] *))) { margin-top: 2rem; margin-bottom: 2rem; } .prose-thead\:border-red-300 :is(:where(thead):not(:where([class~='not-prose'], [class~='not-prose'] *))) { --tw-border-opacity: 1; border-color: rgb(252 165 165 / var(--tw-border-opacity)); } .prose-tr\:border-red-200 :is(:where(tr):not(:where([class~='not-prose'], [class~='not-prose'] *))) { --tw-border-opacity: 1; border-color: rgb(254 202 202 / var(--tw-border-opacity)); } .prose-th\:text-left :is(:where(th):not(:where([class~='not-prose'], [class~='not-prose'] *))) { text-align: left; } .prose-img\:rounded-lg :is(:where(img):not(:where([class~='not-prose'], [class~='not-prose'] *))) { border-radius: 0.5rem; } .prose-video\:my-12 :is(:where(video):not(:where([class~='not-prose'], [class~='not-prose'] *))) { margin-top: 3rem; margin-bottom: 3rem; } .prose-hr\:border-t-2 :is(:where(hr):not(:where([class~='not-prose'], [class~='not-prose'] *))) { border-top-width: 2px; } .prose-lead\:italic :is(:where([class~='lead']):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-style: italic; } ` ) }) }) test('element variants with custom class name', async () => { let config = { plugins: [typographyPlugin({ className: 'markdown' })], content: [ { raw: html`<div class=" markdown markdown-headings:underline markdown-lead:italic markdown-h1:text-3xl markdown-h2:text-2xl markdown-h3:text-xl markdown-h4:text-lg markdown-p:text-gray-700 markdown-a:font-bold markdown-blockquote:italic markdown-figure:mx-auto markdown-figcaption:opacity-75 markdown-strong:font-medium markdown-em:italic markdown-kbd:border-b-2 markdown-code:font-mono markdown-pre:font-mono markdown-ol:pl-6 markdown-ul:pl-8 markdown-li:my-4 markdown-table:my-8 markdown-thead:border-red-300 markdown-tr:border-red-200 markdown-th:text-left markdown-td:align-center markdown-img:rounded-lg markdown-video:my-12 markdown-hr:border-t-2 " ></div>`, }, ], theme: { typography: { DEFAULT: { css: [ { color: 'var(--tw-prose-body)', '[class~="lead"]': { color: 'var(--tw-prose-lead)', }, strong: { color: 'var(--tw-prose-bold)', fontWeight: '600', }, 'h4 strong': { fontWeight: '700', }, }, ], }, }, }, } return run(config).then((result) => { expect(result.css).toMatchFormattedCss( css` ${defaults} .markdown { color: var(--tw-prose-body); } .markdown :where([class~='lead']):not(:where([class~='not-markdown'], [class~='not-markdown'] *)) { color: var(--tw-prose-lead); } .markdown :where(strong):not(:where([class~='not-markdown'], [class~='not-markdown'] *)) { color: var(--tw-prose-bold); font-weight: 600; } .markdown :where(h4 strong):not(:where([class~='not-markdown'], [class~='not-markdown'] *)) { font-weight: 700; } .markdown-headings\:underline :is(:where(h1, h2, h3, h4, h5, h6, th):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { text-decoration-line: underline; } .markdown-h1\:text-3xl :is(:where(h1):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-size: 1.875rem; line-height: 2.25rem; } .markdown-h2\:text-2xl :is(:where(h2):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-size: 1.5rem; line-height: 2rem; } .markdown-h3\:text-xl :is(:where(h3):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-size: 1.25rem; line-height: 1.75rem; } .markdown-h4\:text-lg :is(:where(h4):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-size: 1.125rem; line-height: 1.75rem; } .markdown-p\:text-gray-700 :is(:where(p):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { --tw-text-opacity: 1; color: rgb(55 65 81 / var(--tw-text-opacity)); } .markdown-a\:font-bold :is(:where(a):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-weight: 700; } .markdown-blockquote\:italic :is(:where(blockquote):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-style: italic; } .markdown-figure\:mx-auto :is(:where(figure):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { margin-left: auto; margin-right: auto; } .markdown-figcaption\:opacity-75 :is(:where(figcaption):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { opacity: 0.75; } .markdown-strong\:font-medium :is(:where(strong):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-weight: 500; } .markdown-em\:italic :is(:where(em):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-style: italic; } .markdown-kbd\:border-b-2 :is(:where(kbd):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { border-bottom-width: 2px; } .markdown-code\:font-mono :is(:where(code):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; } .markdown-pre\:font-mono :is(:where(pre):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; } .markdown-ol\:pl-6 :is(:where(ol):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { padding-left: 1.5rem; } .markdown-ul\:pl-8 :is(:where(ul):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { padding-left: 2rem; } .markdown-li\:my-4 :is(:where(li):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { margin-top: 1rem; margin-bottom: 1rem; } .markdown-table\:my-8 :is(:where(table):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { margin-top: 2rem; margin-bottom: 2rem; } .markdown-thead\:border-red-300 :is(:where(thead):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { --tw-border-opacity: 1; border-color: rgb(252 165 165 / var(--tw-border-opacity)); } .markdown-tr\:border-red-200 :is(:where(tr):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { --tw-border-opacity: 1; border-color: rgb(254 202 202 / var(--tw-border-opacity)); } .markdown-th\:text-left :is(:where(th):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { text-align: left; } .markdown-img\:rounded-lg :is(:where(img):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { border-radius: 0.5rem; } .markdown-video\:my-12 :is(:where(video):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { margin-top: 3rem; margin-bottom: 3rem; } .markdown-hr\:border-t-2 :is(:where(hr):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { border-top-width: 2px; } .markdown-lead\:italic :is(:where([class~='lead']):not(:where([class~='not-markdown'], [class~='not-markdown'] *))) { font-style: italic; } ` ) }) }) test('customizing defaults with multiple values does not result in invalid css', async () => { let config = { plugins: [typographyPlugin()], content: [ { raw: html`<div class="prose"></div>`, }, ], theme: { typography: { DEFAULT: { css: { textAlign: ['-webkit-match-parent', 'match-parent'], }, }, }, }, } return run(config).then((result) => { expect(result.css).toMatchFormattedCss( css` ${defaults} .prose { text-align: -webkit-match-parent; text-align: match-parent; } ` ) }) }) it('should be possible to use nested syntax (&) when extending the config', () => { let config = { plugins: [typographyPlugin()], content: [ { raw: html`<div class="prose"></div>`, }, ], theme: { extend: { typography: { DEFAULT: { css: { color: '#000', a: { color: '#888', '&:hover': { color: '#ff0000', }, }, }, }, }, }, }, } return run(config).then((result) => { expect(result.css).toIncludeCss(css` .prose { color: #000; max-width: 65ch; } `) expect(result.css).toIncludeCss(css` .prose :where(a):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: #888; text-decoration: underline; font-weight: 500; } `) expect(result.css).toIncludeCss(css` .prose :where(a):not(:where([class~='not-prose'], [class~='not-prose'] *)):hover { color: #ff0000; } `) }) }) it('should be possible to specify custom h5 and h6 styles', () => { let config = { plugins: [typographyPlugin()], content: [ { raw: html`<div class="prose prose-h5:text-sm prose-h6:text-xl"></div>`, }, ], } return run(config).then((result) => { expect(result.css).toIncludeCss(css` .prose-h5\:text-sm :is(:where(h5):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-size: 0.875rem; line-height: 1.25rem; } .prose-h6\:text-xl :is(:where(h6):not(:where([class~='not-prose'], [class~='not-prose'] *))) { font-size: 1.25rem; line-height: 1.75rem; } `) }) }) it('should not break with multiple selectors with pseudo elements using variants', () => { let config = { darkMode: 'class', plugins: [typographyPlugin()], content: [ { raw: html`<div class="dark:prose"></div>`, }, ], theme: { typography: { DEFAULT: { css: { 'ol li::before, ul li::before': { color: 'red', }, }, }, }, }, } return run(config).then((result) => { expect(result.css).toIncludeCss(css` .dark .dark\:prose :where(ol li, ul li):not(:where([class~='not-prose'], [class~='not-prose'] *))::before { color: red; } `) }) }) it('lifts all common, trailing pseudo elements when the same across all selectors', () => { let config = { darkMode: 'class', plugins: [typographyPlugin()], content: [ { raw: html`<div class="prose dark:prose"></div>`, }, ], theme: { typography: { DEFAULT: { css: { 'ol li::marker::before, ul li::marker::before': { color: 'red', }, }, }, }, }, } return run(config).then((result) => { expect(result.css).toIncludeCss(css` .prose :where(ol li, ul li):not(:where([class~='not-prose'], [class~='not-prose'] *))::marker::before { color: red; } `) // TODO: The output here is a bug in tailwindcss variant selector rewriting // IT should be ::marker::before expect(result.css).toIncludeCss(css` .dark .dark\:prose :where(ol li, ul li):not(:where([class~='not-prose'], [class~='not-prose'] *))::before::marker { color: red; } `) }) }) it('does not modify selectors with differing pseudo elements', () => { let config = { darkMode: 'class', plugins: [typographyPlugin()], content: [ { raw: html`<div class="prose dark:prose"></div>`, }, ], theme: { typography: { DEFAULT: { css: { 'ol li::before, ul li::after': { color: 'red', }, }, }, }, }, } return run(config).then((result) => { expect(result.css).toIncludeCss(css` .prose :where(ol li::before, ul li::after):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: red; } `) // TODO: The output here is a bug in tailwindcss variant selector rewriting expect(result.css).toIncludeCss(css` .dark .dark\:prose :where(ol li, ul li):not(:where([class~='not-prose'], [class~='not-prose'] *))::before, ::after { color: red; } `) }) }) it('lifts only the common, trailing pseudo elements from selectors', () => { let config = { darkMode: 'class', plugins: [typographyPlugin()], content: [ { raw: html`<div class="prose dark:prose"></div>`, }, ], theme: { typography: { DEFAULT: { css: { 'ol li::scroll-thumb::before, ul li::scroll-track::before': { color: 'red', }, }, }, }, }, } return run(config).then((result) => { expect(result.css).toIncludeCss(css` .prose :where(ol li::scroll-thumb, ul li::scroll-track):not(:where([class~='not-prose'], [class~='not-prose'] *))::before { color: red; } `) // TODO: The output here is a bug in tailwindcss variant selector rewriting expect(result.css).toIncludeCss(css` .dark .dark\:prose :where(ol li, ul li):not(:where([class~='not-prose'], [class~='not-prose'] *))::scroll-thumb, ::scroll-track, ::before { color: red; } `) }) }) it('ignores common non-trailing pseudo-elements in selectors', () => { let config = { darkMode: 'class', plugins: [typographyPlugin()], content: [ { raw: html`<div class="prose dark:prose"></div>`, }, ], theme: { typography: { DEFAULT: { css: { 'ol li::before::scroll-thumb, ul li::before::scroll-track': { color: 'red', }, }, }, }, }, } return run(config).then((result) => { expect(result.css).toIncludeCss(css` .prose :where(ol li::before::scroll-thumb, ul li::before::scroll-track):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: red; } `) // TODO: The output here is a bug in tailwindcss variant selector rewriting expect(result.css).toIncludeCss(css` .dark .dark\:prose :where(ol li::scroll-thumb, ul li::scroll-track):not(:where([class~='not-prose'], [class~='not-prose'] *))::before, ::before { color: red; } `) }) }) test('lead styles are inserted after paragraph styles', async () => { let config = { content: [{ raw: html`<div class="prose"></div>` }], } return run(config).then((result) => { expect(result.css).toIncludeCss( css` .prose { color: var(--tw-prose-body); max-width: 65ch; } .prose :where(p):not(:where([class~='not-prose'], [class~='not-prose'] *)) { margin-top: 1.25em; margin-bottom: 1.25em; } .prose :where([class~='lead']):not(:where([class~='not-prose'], [class~='not-prose'] *)) { color: var(--tw-prose-lead); font-size: 1.25em; line-height: 1.6; margin-top: 1.2em; margin-bottom: 1.2em; } ` ) }) })
Edit
Download
Unzip
Chmod
Delete