diff --git a/README.md b/README.md index cd7e1cc..35ab854 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,77 @@ # Remarkdown -Remarkdown styles HTML to look like plain Markdown text. +Remarkdown makes HTML look like plain [Markdown][] text. -- [online demo and docs][docs] -- [npm: remarkdown.css][npm] +- Npm: [remarkdown.css](https://www.npmjs.com/package/remarkdown.css) +- Documentation: + - [Using Remarkdown](https://fvsch.github.io/remarkdown/) + - [Remarkdown styles](https://fvsch.github.io/remarkdown/styles) + - [Configuring Remarkdown](https://fvsch.github.io/remarkdown/config) -Markdown is [a plain-text syntax by John Gruber][markdown]. Some styles are inspired by [PHP Markdown Extra][md-extra] and [GitHub Flavored Markdown][md-gfm]. +## Usage with a CDN -## Documentation +Add a link to the `dist/remarkdown.css` stylesheet and `class="remarkdown"` on a container wrapping all the text you want to style: -- [Using Remarkdown][docs] -- [Available styles][styles] -- [Customizing Remarkdown][customize] +```html + + + + Using Remarkdown + + + +

Hello World

+

A paragraph.

+ + +``` +There are a few [alternate styles](https://fvsch.github.io/remarkdown/styles) you can pick from. For example, `class="remarkdown h1-line ul-star"` enables underlined `

`s and asterisks for bullets. -[npm]: https://www.npmjs.com/package/remarkdown.css -[docs]: https://fvsch.github.io/remarkdown/ -[styles]: https://fvsch.github.io/remarkdown/styles.html -[customize]: https://fvsch.github.io/remarkdown/customize.html -[markdown]: https://daringfireball.net/projects/markdown/ -[md-extra]: https://michelf.ca/projects/php-markdown/extra/ -[md-gfm]: https://github.github.com/gfm/ +The main `dist/remarkdown.css` stylesheet exists in a few variants, which tweak what CSS selectors look like and what styles are enabled by default: + +- [remarkdown.css](): normal defaults, `.remarkdown` class. +- [remarkdown.scope.css](https://cdn.jsdelivr.net/npm/remarkdown.css@4/dist/remarkdown.css): normal defaults, `.remarkdown` class using [CSS `@scope`][css-scope]. +- + +## Usage with npm + +```sh +npm install remarkdown.css +``` + +When using a Bundler like [Vite][], you should be able to import pre-built stylesheets from the package’s `dist` directory in your own CSS: + +```css +@import "remarkdown.css/dist/remarkdown.css"; +/* or "remarkdown.css/dist/remarkdown-zero.attr.css", etc. */ +``` + +Beyond the pre-built stylesheets, Remarkdown is a [Sass][] library, and can be imported with `@use` then configured with the `config()` mixin: + +```scss +@use "pkg:remarkdown.css" as rmd; + +// Configure a custom Remarkdown build +@include rmd.config($line-height: 1.75); + +// Generate styles +@include rmd.header(); +@include rmd.styles(); +``` + +See the [`preset` directory](https://github.com/fvsch/remarkdown/tree/main/preset) for some examples that modify the Remarkdown selectors, and [`lib/config/_defaults.scss`](https://github.com/fvsch/remarkdown/tree/main/lib/config/_defaults.scss) for all available configuration options. + + + +[Markdown]: https://daringfireball.net/projects/markdown/ +[Sass]: https://sass-lang.com/ +[Vite]: https://vite.dev/ +[css-scope]: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@scope + +[remarkdown.css]: https://cdn.jsdelivr.net/npm/remarkdown.css@4/dist/remarkdown.css +[remarkdown.attr.css]: https://cdn.jsdelivr.net/npm/remarkdown.css@4/dist/remarkdown.css +[remarkdown.scope.css]: https://cdn.jsdelivr.net/npm/remarkdown.css@4/dist/remarkdown.css +[remarkdown.css]: https://cdn.jsdelivr.net/npm/remarkdown.css@4/dist/remarkdown.css +[remarkdown.css]: https://cdn.jsdelivr.net/npm/remarkdown.css@4/dist/remarkdown.css +[remarkdown.css]: https://cdn.jsdelivr.net/npm/remarkdown.css@4/dist/remarkdown.css diff --git a/dist/remarkdown-zero.attr.css b/dist/remarkdown-zero.attr.css index 85c9a33..651278d 100644 --- a/dist/remarkdown-zero.attr.css +++ b/dist/remarkdown-zero.attr.css @@ -30,23 +30,23 @@ font-family: var(--rmd-code-font); } -[data-remarkdown~="a-bracket"] a { +[data-remarkdown~="a-bracket"] :any-link { text-decoration-inset: 1ch; } -[data-remarkdown~="a-bracket"] a::before { +[data-remarkdown~="a-bracket"] :any-link::before { content: "["/""; } -[data-remarkdown~="a-bracket"] a::after { +[data-remarkdown~="a-bracket"] :any-link::after { content: "]"/""; } -[data-remarkdown~="a-showurl"] a { +[data-remarkdown~="a-showurl"] :any-link { text-decoration-inset: 0; } -[data-remarkdown~="a-showurl"] a[href]::before { +[data-remarkdown~="a-showurl"] :any-link::before { content: "["/""; } -[data-remarkdown~="a-showurl"] a[href]::after { +[data-remarkdown~="a-showurl"] :any-link::after { content: "](" attr(href) ")"/""; word-break: break-all; } diff --git a/dist/remarkdown-zero.css b/dist/remarkdown-zero.css index d0ace11..8d3548f 100644 --- a/dist/remarkdown-zero.css +++ b/dist/remarkdown-zero.css @@ -29,22 +29,22 @@ :is(pre, code, kbd, samp) { font-family: var(--rmd-code-font); } - :scope.a-bracket a { + :scope.a-bracket :any-link { text-decoration-inset: 1ch; } - :scope.a-bracket a::before { + :scope.a-bracket :any-link::before { content: "["/""; } - :scope.a-bracket a::after { + :scope.a-bracket :any-link::after { content: "]"/""; } - :scope.a-showurl a { + :scope.a-showurl :any-link { text-decoration-inset: 0; } - :scope.a-showurl a[href]::before { + :scope.a-showurl :any-link::before { content: "["/""; } - :scope.a-showurl a[href]::after { + :scope.a-showurl :any-link::after { content: "](" attr(href) ")"/""; word-break: break-all; } diff --git a/dist/remarkdown.attr.css b/dist/remarkdown.attr.css index 95a94c0..594cc8c 100644 --- a/dist/remarkdown.attr.css +++ b/dist/remarkdown.attr.css @@ -30,23 +30,23 @@ font-family: var(--rmd-code-font); } -[data-remarkdown] a { +[data-remarkdown] :any-link { text-decoration-inset: 1ch; } -[data-remarkdown] a::before { +[data-remarkdown] :any-link::before { content: "["/""; } -[data-remarkdown] a::after { +[data-remarkdown] :any-link::after { content: "]"/""; } -[data-remarkdown~="a-showurl"] a { +[data-remarkdown~="a-showurl"] :any-link { text-decoration-inset: 0; } -[data-remarkdown~="a-showurl"] a[href]::before { +[data-remarkdown~="a-showurl"] :any-link::before { content: "["/""; } -[data-remarkdown~="a-showurl"] a[href]::after { +[data-remarkdown~="a-showurl"] :any-link::after { content: "](" attr(href) ")"/""; word-break: break-all; } diff --git a/dist/remarkdown.css b/dist/remarkdown.css index e2faf6d..4e50f6a 100644 --- a/dist/remarkdown.css +++ b/dist/remarkdown.css @@ -29,22 +29,22 @@ :is(pre, code, kbd, samp) { font-family: var(--rmd-code-font); } - a { + :any-link { text-decoration-inset: 1ch; } - a::before { + :any-link::before { content: "["/""; } - a::after { + :any-link::after { content: "]"/""; } - :scope.a-showurl a { + :scope.a-showurl :any-link { text-decoration-inset: 0; } - :scope.a-showurl a[href]::before { + :scope.a-showurl :any-link::before { content: "["/""; } - :scope.a-showurl a[href]::after { + :scope.a-showurl :any-link::after { content: "](" attr(href) ")"/""; word-break: break-all; } diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 0000000..c680260 --- /dev/null +++ b/docs/404.html @@ -0,0 +1,37 @@ + + + + + Remarkdown — Page not found + + + + + + + + +
+ +

+ Page not found +

+ +

+ Nothing to see here. +

+ +
+ + + diff --git a/docs/config.html b/docs/config.html new file mode 100644 index 0000000..7520780 --- /dev/null +++ b/docs/config.html @@ -0,0 +1,302 @@ + + + + + Configuring Remarkdown + + + + + + + + +
+ +

+ Configuring Remarkdown +

+ +

+ There are three ways to customize Remarkdown: +

+ +
    +
  1. HTML classes or attributes
  2. +
  3. CSS variables and overrides
  4. +
  5. Custom Sass builds
  6. +
+ +

+ Picking optional styles in HTML +

+ +

+ Enable optional styles by combining the remarkdown class and the optional style classes: +

+ +
<div class="remarkdown h1-line ul-star">
+	<h1>Using remarkdown.css</h1>
+	…
+</div>
+ +

+ With the remarkdown.attr.css variant, use the data-remarkdown attribute instead: +

+ +
<div data-remarkdown="h1-line ul-star">
+	<h1>Using remarkdown.attr.css</h1>
+	…
+</div>
+ +

+ If you don’t want to use Remarkdown’s default styles, you can use the remarkdown-zero.css stylesheet instead (or remarkdown-zero.attr.css), and declare all the styles you want explicitly. +

+ +
<div class="remarkdown hn-prefix h1-line ul-plus ol-alpha a-bracket pre-tick quote-mark …">
+	<h1>Using remarkdown-zero.css</h1>
+	…
+</div>
+ +

+ CSS variables and overrides +

+ +

+ Remarkdown defines a few CSS variables: +

+ +
.remarkdown {
+	--rmd-font: ui-monospace, monospace;
+	--rmd-code-font: inherit;
+	--rmd-line-height: 1.5;
+	--rmd-hn-prefix: "#";
+	--rmd-h1-line: "====================";
+	--rmd-h2-line: "--------------------";
+	--rmd-hr-stars: "* * * *";
+	--rmd-hr-dashes: "-------";
+	--rmd-pre-ticks: "```";
+	--rmd-pre-tilde: "~~~";
+	--rmd-pre-tilde-line: "~~~~~~~~~~~~~~~~~~~~";
+	--rmd-quote-mark: ">\a>\a>\a>\a>\a>\a>\a>\a>\a>\a";
+	--rmd-quote-rtl: "<\a<\a<\a<\a<\a<\a<\a<\a<\a<\a";
+	--rmd-table-vline: "|\a|\a|\a|\a|\a|\a|\a|\a|\a|\a";
+	--rmd-table-hline: "--------------------";
+}
+ +

+ Use these variables to tweak Remarkdown’s font-family, or the characters used in syntax markers. +

+ +
.remarkdown {
+	--rmd-font: Consolas, Menlo, ui-monospace, monospace;
+	--rmd-h2-line: "~~~~~~~~~~~~~~~~~~~~";
+	--rmd-hr-stars: "*_* *_* *_*";
+}
+ +

+ Note that colors and other cosmetic styles are not handled by these CSS variables. If you want to tweak colors and more, you will need to write your own style overrides. Here are a few examples. +

+ +

+ Make markers pop?! +

+ +
.remarkdown {
+	::before,
+	::after {
+		color: hsl(190 44% 36%);
+		text-shadow: 1px 2px hsl(190 66% 75%);
+		opacity: 0.75;
+	}
+}
+ +

+ Make titles big again +

+ +

+ Remarkdown sets the font-size of headings to 100% to better achieve that plain text look (since most plain text editors and file formats use the same font size for all text). But if you want big titles anyway, it’s easy: +

+ +
.remarkdown {
+	h1 { font-size: 1.5rem; font-weight: bold; }
+	h2 { font-size: 1.2rem; }
+}
+ +

+ (Alternatively, if you’re compiling your own build, you can remove the hn-reset style from $defaults.) +

+ +

+ Beware of selector specificity +

+ +

+ Most Remarkdown styles have a selector specificity of 0,0,1,1 or in some cases 0,0,1,2. If your own selectors have similar weight, make sure you declare your own styles after remarkdown.css: +

+ +
@import "remarkdown.css/dist/remarkdown.css";
+/* Includes this selector (specificity 0,0,1,1):
+.remarkdown h1 { margin-block: 1.5lh 1lh; } */
+
+/* Your style overrides (same specificity): */
+.remarkdown h1 { margin-block: 0; }
+
+ +

+ Alternatively, you can use CSS Layers to put all Remarkdown styles in a cascade layer with lower precedence: +

+ +
@import "remarkdown.css/dist/remarkdown.css" layer(remarkdown);
+/* Includes this selector (specificity 0,0,1,1):
+.remarkdown h1 { margin-block: 1.5lh 1lh; } */
+
+/* Your unlayered styles have priority,
+despite the lower specificity (0,0,0,1): */
+h1 { margin-block: 0; }
+
+ +

+ Build a custom stylesheet with Sass +

+ +

+ Remarkdown is written in Sass, published to npm, and can be imported as a Sass library to generate a custom Remarkdown variant with your own config. +

+ +

+ This part assumes that you are using Node.js and a toolchain that supports Sass, whether that’s npm and the sass package directly, or a bundler like Vite. +

+ +

+ Install Remarkdown and Sass using npm (or pnpm or a similar package manager): +

+ +
npm install sass remarkdown.css
+ +

+ Then you should be able to import remarkdown.css in any .scss stylesheet: +

+ +
@import "pkg:remarkdown.css" as rmd;
+@include rmd.header();
+@include rmd.styles();
+ +

+ If the remarkdown.css package is installed but Sass cannot find pkg:remarkdown.css, you may need to configure Sass to resolve pkg: specifiers. Or you can fall back to relative imports instead: +

+ +
@import "./path/to/node_modules/remarkdown.css" as rmd;
+@include rmd.header();
+@include rmd.styles();
+ +

+ Example: custom selectors +

+ +

+ Remarkdown selectors can be configured with the config() mixin and the $root-selector and $option-selector variables. The default build of Remarkdown uses this configuration: +

+ +
@import "pkg:remarkdown.css" as rmd;
+
+@include rmd.config(
+	$root-selector: ".remarkdown",
+	$option-selector: ".remarkdown.%s"
+);
+
+@include rmd.header();
+@include rmd.styles();
+ +

+ Note that in $option-selector, the substring %s will be replaced by the optional style’s name, so that .remarkdown.%s becomes, for example, .remarkdown.h1-line. +

+ +

+ Here is a somewhat contrived example that uses CSS nesting and data-* attributes: +

+ +
@import "pkg:remarkdown.css" as rmd;
+
+@include rmd.config(
+	$root-selector: "&",
+	$option-selector: "&[data-%s]"
+);
+
+@include rmd.header();
+
+[data-remarkdown] {
+	@include rmd.styles();
+}
+ +

+ Example: selecting default styles +

+ +

+ Use the $defaults variable to change the styles that should be applied by default (i.e. styles that will be defined using the $root-selector instead of the $option-selector). +

+ +
@import "pkg:remarkdown.css" as rmd;
+
+@include rmd.config(
+	$defaults: (
+		hn-reset,
+		hn-prefix,
+		h1-line,
+		h2-line,
+		ul-star,
+		ol-alpha,
+		strong-reset,
+		strong-underscore,
+		hr-dash,
+		hr-center
+	)
+);
+
+@include rmd.header();
+@include rmd.styles();
+ +

+ You will need to list all the styles you want to see apply by default. That can be a fairly long list (as above). For instance, if I omit both em-star and em-underscore, EM elements won’t have any visible markers. +

+ +

+ All styles not listed in $defaults will still be part of the CSS output, but they will use an optional style selector, and so will need enabling in HTML: +

+ +
<div class="remarkdown em-reset em-star">
+	<p>Hello <em>world</em>!</p>
+</div>
+ +

+ Example: limit or remove optional styles +

+ +

+ If you customize the default Remarkdown styles with $defaults, you may not need the remaining styles in the CSS output at all. This is where the $options variable comes in. +

+ +

+ You can explicitly list all styles that should be +

+ +

+ By setting the $rmd-output-all-styles option to false, Remarkdown will only output those styles that are listed in $rmd-defaults. This can be useful if you want to pick a set of default styles, don’t intend to ever use the alternative styles, and want to make a smaller CSS file. +

+ +

+ Currently the default build with all styles included is close to 8 KB, while the same defaults with no alternative styles included is down to 4 KB. +

+ +
+ + + diff --git a/docs/customize.html b/docs/customize.html deleted file mode 100644 index ec82c8b..0000000 --- a/docs/customize.html +++ /dev/null @@ -1,214 +0,0 @@ - - - - - Remarkdown — Configuration - - - - - - - -
- -

- Customizing Remarkdown -

- -

- There are three ways to customize Remarkdown: picking available styles in HTML, adding your own CSS styles, and building Remarkdown with Sass and custom settings. -

- -

- Picking styles in HTML -

- -

- You can activate the available styles by declaring them in your HTML, as classes or attribute values: -

- -
<div class="remarkdown h1-line ul-star">
-	<p>Using remarkdown.css</p>
-</div>
- -

- Remarkdown Zero -

- -

- If you don’t want to use Remarkdown’s default styles, you can use the remarkdown-zero.css stylesheet instead, and declare all the styles you want explicitly. -

- -
<div class="remarkdown hn-reset hn-prefix h1-line ul-star a-bracket pre-tick quote-mark …">
-	<p>Using remarkdown.css</p>
-</div>
- -

- Using attributes instead of classes -

- -

- Remarkdown comes with an alternative remarkdown.attr.css stylesheet which uses the data-remarkdown attribute for styling, instead of classes: -

- -
<head>
-	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/remarkdown.css/dist/remarkdown.attr.css">
-	<!-- or https://cdn.jsdelivr.net/npm/remarkdown.css/dist/remarkdown-zero.attr.css -->
-<body data-remarkdown="h1-line a-bracket ul-plus ol-alpha">
-	…
-</body>
- -

- Add your own CSS -

- -

- I strive to make Remarkdown free of cosmetic choices. For instance, the layout and colors of this demo are not handled by remarkdown.css. So you probably want to add a few CSS styles of your own to make the end result prettier. Here are a few suggestions. -

- -

- Make markers pop out -

- -
.remarkdown ::before,
-.remarkdown ::after {
-	color: hsl(0 100% 100% / 0.5);
-}
- -

- Specify you own fonts -

- -

- Remarkdown tells browsers to use their default monospace font, but you can always specify your own, as I did here: -

- -
.remarkdown {
-	font-family: Menlo, DejaVu Sans Mono, Consolas, monospace;
-}
- -

- In this example I’m using two different monospace font-stacks, one without serifs (Menlo etc.) and one with slab serifs (Courier etc.), to differentiate between ordinary text and code blocks. -

- -

- You could also use a font that is not monospace. A variable-width font whose design is not too tight could give interesting results. -

- -

- Make titles big again -

- -

- Remarkdown sets the font-size of headings to 1em to better achieve that plain text look (since plain text editors and file formats use the same font size for all text). But if you want big titles anyway, it’s easy: -

- -
.remarkdown h1 { font-size: 1.5em; }
-.remarkdown h2 { font-size: 1.2em; }
- -

- Alternatively, if you’re compiling your own build, you can remove the hn-reset style from $default-styles. -

- -

- Beware of selector specificity -

- -

- Most Remarkdown styles have a selector specificity of 0,0,1,1 or in some cases 0,0,1,2. If your own selectors have similar weight, make sure you declare your own styles after remarkdown.css: -

- -
@import "remarkdown.css/dist/remarkdown.css";
-/* Includes this selector (specificity 0,0,1,1):
-.remarkdown h1 {margin-block: 1.5lh 1lh} */
-
-/* Your style overrides (same specificity): */
-.remarkdown h1 {margin-block: 0}
-
- -

- Alternatively, you can use CSS Layers to put all Remarkdown styles in a cascade layer with lower precedence: -

- -
@import "remarkdown.css/dist/remarkdown.css" layer(remarkdown);
-/* Includes this selector (specificity 0,0,1,1):
-.remarkdown h1 {margin-block: 1.5lh 1lh} */
-
-/* Your unlayered styles have priority,
-despite the lower specificity (0,0,0,1): */
-h1 {margin-block: 0}
-
- -

- Build a custom stylesheet with Sass -

- -

- You can recompile Remarkdown using Sass, and there are a number of options (Sass variables) that you can use to alter the CSS output of your custom build. -

- -

- Things you will need: -

- -
    -
  1. Node.js installed
  2. -
  3. The Remarkdown source (zip)
  4. -
- -

- Once you’ve downloaded and extracted the Remarkdown source, modify the src/remardown-custom.scss stylesheets, changing or adding $rmd-* variables to change your build’s configuration. See src/_options.scss for a detailed list of options. -

- -

- To compile, use a terminal (Terminal on macOS, cmd.exe or Git Bash on Windows, etc.), navigate to the root of the extracted folder (probably called remarkdown-main), and run these commands: -

- -
npm install
-npm run build
- -

- Note: if you already have your own Node-and-Sass build chain in place, you can install Remarkdown with npm install remarkdown.css and import the main mixins with @import "node_modules/remarkdown.css/src/_imports.scss";. -

- -

- Example: changing the default styles -

- -

- You can use the $rmd-defaults variable to change the styles that should be applied by default (i.e. when you use the remarkdown class without any explicit style). -

- -
$rmd-defaults:
-	hn-reset hn-prefix h1-line h2-line
-	ul-star ol-decimal
-	em-underscore strong-underscore
-	hr-dash hr-center;
- -

- You will need to list all the styles you want to see apply by default. That can be a fairly long list (as above). For instance, if I omit both em-star and em-underscore, EM elements won’t have any visible marker (unless we explicitly set one of those styles in the HTML code). -

- -

- Example: only output default styles -

- -

- By setting the $rmd-output-all-styles option to false, Remarkdown will only output those styles that are listed in $rmd-defaults. This can be useful if you want to pick a set of default styles, don’t intend to ever use the alternative styles, and want to make a smaller CSS file. -

- -

- Currently the default build with all styles included is close to 8 KB, while the same defaults with no alternative styles included is down to 4 KB. -

- -
- - - diff --git a/docs/docs.css b/docs/docs.css index 27f7885..0d0172e 100644 --- a/docs/docs.css +++ b/docs/docs.css @@ -41,22 +41,22 @@ :is(pre, code, kbd, samp) { font-family: var(--rmd-code-font); } - a { + :any-link { text-decoration-inset: 1ch; } - a::before { + :any-link::before { content: "["/""; } - a::after { + :any-link::after { content: "]"/""; } - :scope.a-showurl a { + :scope.a-showurl :any-link { text-decoration-inset: 0; } - :scope.a-showurl a[href]::before { + :scope.a-showurl :any-link::before { content: "["/""; } - :scope.a-showurl a[href]::after { + :scope.a-showurl :any-link::after { content: "](" attr(href) ")"/""; word-break: break-all; } @@ -456,22 +456,22 @@ [data-remarkdown] :is(pre, code, kbd, samp) { font-family: var(--rmd-code-font); } - [data-remarkdown~="a-bracket"] a { + [data-remarkdown~="a-bracket"] :any-link { text-decoration-inset: 1ch; } - [data-remarkdown~="a-bracket"] a::before { + [data-remarkdown~="a-bracket"] :any-link::before { content: "["/""; } - [data-remarkdown~="a-bracket"] a::after { + [data-remarkdown~="a-bracket"] :any-link::after { content: "]"/""; } - [data-remarkdown~="a-showurl"] a { + [data-remarkdown~="a-showurl"] :any-link { text-decoration-inset: 0; } - [data-remarkdown~="a-showurl"] a[href]::before { + [data-remarkdown~="a-showurl"] :any-link::before { content: "["/""; } - [data-remarkdown~="a-showurl"] a[href]::after { + [data-remarkdown~="a-showurl"] :any-link::after { content: "](" attr(href) ")"/""; word-break: break-all; } diff --git a/docs/index.html b/docs/index.html index 926e0a6..e66b8c4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -4,16 +4,17 @@ Remarkdown makes HTML look like plain Markdown text + - +
@@ -23,47 +24,114 @@

- The text you’re reading right now is not plain text, but semantic HTML styled like Markdown text. Right-click and inspect this page to get a feel for how it works. + This is a <p> element. That heading above is a real <h1>. Normal semantic HTML, styled to look like plain text. +

+ +

+ That’s Remarkdown in a nutshell: a stylesheet that styles HTML to look like Markdown text. I’m not sure it’s useful, I’ve rarely used it myself; I simply made it because I could.

- Using Remarkdown + Usage with a CDN

- It’s as simple as downloading remarkdown.css, and adding the remarkdown class to a container: + You can use Remarkdown with a CDN like jsDelivr (recommended) or unpkg. When using the main remarkdown.css stylesheet, you will need to add class="remarkdown" on a container wrapping all the text you want to style, like this:

<!doctype html>
 <html lang="en">
 <head>
 	<title>Using Remarkdown</title>
-	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/remarkdown.css/dist/remarkdown.css">
+	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/remarkdown.css@4/dist/remarkdown.css">
 </head>
-<body class="remarkdown">
+<body class="remarkdown">
 	<h1>Hello World</h1>
 	<p>A paragraph.</p>
 </body>
 </html>
+

+ There are a few alternate styles you can pick from. For example, class="remarkdown h1-line ul-star" enables underlined <h1>s and asterisks for bullets. +

+

- Make it your own + Usage with npm

- Remarkdown is broken down into a list of styles, and the default remarkdown.css uses a dozen of them by default. + Remarkdown is published on npm as remarkdown.css. If you’re working in a project using npm (or pnpm or yarn, etc.) and maybe a code bundler like Vite, you should be able to install Remarkdown like so: +

+ +
npm install remarkdown.css
+ +

+ Then in your JavaScript modules or CSS files, import one of the compiled stylesheets in the package’s dist folder:

+
// In a JS module processed by a bundler
+import "remarkdown.css/dist/remarkdown.css";
+ +
/* Or in a CSS file processed by a bundler */
+@import "remarkdown.css/dist/remarkdown.css";
+
+

- You can choose additional variants by adding option names as classes along the remarkdown class. For example: + The remarkdown.css package is also a Sass library, which can be used in Sass modules (.scss files):

-
<body class="remarkdown h1-line pre-tick">
-	…
-</body>
+
@use "pkg:remarkdown.css" as rmd;
+@include rmd.header();
+@include rmd.styles();
+

- Take a look at the customize page for different ways to disable, enable or override styles, change the defaults, change how selectors look, etc. + Using it with Sass lets you customize the output further, especially when using the `rmd.config()` mixin. See “Build a custom stylesheet with Sass” for details. +

+ +

+ Remarkdown variants +

+ +

+ The pre-built Remarkdown stylesheet comes in a few variants, which change: + what default styles are enabled; how optional styles are declared in HTML. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
StylesheetDefaultsSelector style
remarkdown.cssnormal@scope (.remarkdown)
remarkdown-zero.cssresets only@scope (.remarkdown)
remarkdown.attr.cssnormaldata-remarkdown="…"
remarkdown-zero.attr.cssresets onlydata-remarkdown="…"
+ +

+ You can make your own variant using the Sass library with custom values for $selectors, $defaults and $options.

@@ -72,22 +140,34 @@

+

+ Similar projects +

+ +

+ As far as I’m aware, when I created Remarkdown v1 in 2011, it was the first published implementation of this strange idea of styling HTML like Markdown. +

+ +

+ Other implementations include Peter Coles’ Markdown.css, and many developer homepages which do some Markdown-ish styling of headings and a few other elements. +

+
diff --git a/docs/styles.html b/docs/styles.html index d879de1..528a26d 100644 --- a/docs/styles.html +++ b/docs/styles.html @@ -2,8 +2,9 @@ - Remarkdown — Available styles + Remarkdown styles + @@ -13,184 +14,192 @@
about styles - customize + config
-

Demo of available styles

+

+ Remarkdown styles +

+ +

+ Here are all the styles provided by Remarkdown. The “default” ones are enabled in the remarkdown.css stylesheet.

+

- All styles that are listed as “Default” here will be active when using the default remarkdown.css build. - No need to specify class="remarkdown style-name" for those. - On the other hand, if you want to either disable them or include them in a custom build, it’s helpful to know the style’s name and what it does. + You can also use remarkdown-zero.css which has no default styles, and enable styles with HTML classes. And for more control, you can use Sass to make a custom build with your prefered default styles.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Option nameDefaultDescription
base-textYesSet a monospace font style
hn-resetYesReset heading text styles
hn-prefixYesAdd “#” characters before headings
h1-line—Underline H1s with equals
h2-line—Underline H2s with hyphens
ol-decimalYesUse decimal numbers for OL items
ol-zero—Use zeroes for all OL items
ol-alpha—Use lowercase letters for OL items
ul-dashYesUse “-” for bullets
ul-star—Use “*” for bullets
ul-plus—Use “+” for bullets
quote-mark—Use “>” signs for blockquotes
a-bracketYesAdd square brackets around links.
a-showurl—Show URLs after links
em-resetYesReset EM font style
em-starYesSurround EMs with “*”
em-underscore—Surround EMs with “_”
strong-resetYesReset STRONG font style
strong-starYesSurround STRONGs with “**”
strong-underscore—Surround STRONGs with “__”
code-tickYesWrap CODE in “`” (backticks)
pre-indentYesLeft indent PRE elements
pre-tick—Wrap PRE in “```”
pre-tilde—Wrap PRE in “~~~”
pre-tilde-full—Wrap PRE in a full line of “~”
hr-starYesUse “*” signs for HRs
hr-dash—Use “-” signs for HRs
hr-center—Horizontally center HRs
del-tilde—Wrap DEL in “~~” (GFM style)
table-reset—Plain text rendering for TABLE
table-border—Use “|” and “-” for TABLE borders
table-border-full—Add left and right borders
Style nameDefaultDescription
base-textYesSet a monospace font style
hn-resetYesReset heading text styles
hn-prefixYesAdd “#” characters before headings
h1-line—Underline H1s with equals
h2-line—Underline H2s with hyphens
ol-decimalYesUse decimal numbers for OL items
ol-zero—Use zeroes for all OL items
ol-alpha—Use lowercase letters for OL items
ul-dashYesUse “-” for bullets
ul-star—Use “*” for bullets
ul-plus—Use “+” for bullets
quote-mark—Use “>” signs for blockquotes
a-bracketYesAdd square brackets around links.
a-showurl—Show URLs after links
em-resetYesReset EM font style
em-starYesSurround EMs with “*”
em-underscore—Surround EMs with “_”
strong-resetYesReset STRONG font style
strong-starYesSurround STRONGs with “**”
strong-underscore—Surround STRONGs with “__”
code-tickYesWrap CODE in “`” (backticks)
pre-indentYesLeft indent PRE elements
pre-tick—Wrap PRE in “```”
pre-tilde—Wrap PRE in “~~~”
pre-tilde-full—Wrap PRE in a full line of “~”
hr-starYesUse “*” signs for HRs
hr-dash—Use “-” signs for HRs
hr-center—Horizontally center HRs
del-tilde—Wrap DEL in “~~” (GFM style)
table-reset—Plain text rendering for TABLE
table-border—Use “|” and “-” for TABLE borders
table-border-full—Add left and right borders

@@ -210,7 +219,8 @@

Headings

-

+ +

Defaults: hn-reset and hn-prefix

@@ -378,7 +388,8 @@

Emphasis

-

+ +

Default: em-reset and em-star

@@ -394,7 +405,8 @@

This is literally figurative!

-

+ +

Default: strong-reset and strong-star

diff --git a/lib/styles/_link.scss b/lib/styles/_link.scss index 5ba9fb6..deba3f6 100644 --- a/lib/styles/_link.scss +++ b/lib/styles/_link.scss @@ -5,27 +5,29 @@ @mixin link { @include option(a-bracket) { - a { + // Avoid the selector `a` alone, because we don't want to style + // anchors like ``. + :any-link { text-decoration-inset: 1ch; - } - a::before { - @include content("["); - } - a::after { - @include content("]"); + &::before { + @include content("["); + } + &::after { + @include content("]"); + } } } @include option(a-showurl) { - a { + :any-link { text-decoration-inset: 0; - } - a[href]::before { - @include content("["); - } - a[href]::after { - @include content("](" attr(href) ")"); - word-break: break-all; + &::before { + @include content("["); + } + &::after { + @include content("](" attr(href) ")"); + word-break: break-all; + } } } } diff --git a/package.json b/package.json index 420eceb..e2bd7ba 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "name": "remarkdown.css", "version": "4.0.0-beta.2", - "license": "MIT", "description": "Remarkdown makes HTML look like plain Markdown text", "homepage": "https://fvsch.github.io/remarkdown/", + "license": "MIT", + "author": "Florens Verschelde", "repository": { "type": "git", "url": "https://github.com/fvsch/remarkdown.git" }, - "author": "Florens Verschelde", "keywords": [ "css", "sass", @@ -45,8 +45,9 @@ }, "devDependencies": { "@types/node": "^24.13.5", - "oxfmt": "^0.71.0", - "postcss": "^8.5.28", + "happy-dom": "^20.14.6", + "oxfmt": "^0.72.0", + "postcss": "^8.5.29", "postcss-combine-duplicated-selectors": "^11.0.0", "sass": "^1.105.1", "servitsy": "^0.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3bcaabc..f1b3cdb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,15 +11,18 @@ importers: '@types/node': specifier: ^24.13.5 version: 24.13.5 + happy-dom: + specifier: ^20.14.6 + version: 20.14.6 oxfmt: - specifier: ^0.71.0 - version: 0.71.0 + specifier: ^0.72.0 + version: 0.72.0 postcss: - specifier: ^8.5.28 - version: 8.5.28 + specifier: ^8.5.29 + version: 8.5.29 postcss-combine-duplicated-selectors: specifier: ^11.0.0 - version: 11.0.0(postcss@8.5.28) + version: 11.0.0(postcss@8.5.29) sass: specifier: ^1.105.1 version: 1.105.1 @@ -31,7 +34,7 @@ importers: version: 7.0.2 vitest: specifier: ^5.0.3 - version: 5.0.3(@types/node@24.13.5)(vite@8.3.0(@types/node@24.13.5)(sass@1.105.1)) + version: 5.0.3(@types/node@24.13.5)(happy-dom@20.14.6)(vite@8.3.0(@types/node@24.13.5)(sass@1.105.1)) packages: @@ -48,124 +51,124 @@ packages: '@oxc-project/types@0.150.0': resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} - '@oxfmt/binding-android-arm-eabi@0.71.0': - resolution: {integrity: sha512-l31EyBfB4egJeFua11aQFEUE9nnDtfpWCadqQovm0WL30t/yUHtdFl/5HyFXvd5xT8Ewh3U4TI9f30GowrQ1fw==} + '@oxfmt/binding-android-arm-eabi@0.72.0': + resolution: {integrity: sha512-u4U1uaDOVBuorGNC/07NLm48nECHOiJJka002YGML16Md2DRvf38RGBt01lH60EOGvNlFVmbLoksQedNBoZHKw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.71.0': - resolution: {integrity: sha512-E8q39SUZzSXQ08oZoFoTkxRADkdBNgL6De4qgZYr2O4JCunwqrZwAyCebxIF3yVHBafHM30WUkhTkDi+jNYZOw==} + '@oxfmt/binding-android-arm64@0.72.0': + resolution: {integrity: sha512-GCnM+Ae2mWOKC+92y3FOb6eZJeVnBAVz82e3lITUUg+kxukYKzBNAvJ/hhHIfa2A0md55SYYhdWPddSSy3KPVA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.71.0': - resolution: {integrity: sha512-pTteTrN88DicrmJ3DocBmpNDa6Umfh0iveEG8rnAlDFQpclmtDS7hnIvZbIEV9mDO0Ot1U0sjtoJQ1itd/ZMhQ==} + '@oxfmt/binding-darwin-arm64@0.72.0': + resolution: {integrity: sha512-LjQ8tlevdtwraFlWC6AnQ1Forzde0clRmnsP3QmK+ydSElDt/J+5Jc+m+//Ly1G97aP7WCNUw110QMIUw2K7xA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.71.0': - resolution: {integrity: sha512-G5melRC3IiNEgXlNKJhSExJYQGAQIppHFp2cl/wFrqQcEwmc4k4TkrHaFTR29SDudjoW5AZq5cXPGJDqtlH/Yg==} + '@oxfmt/binding-darwin-x64@0.72.0': + resolution: {integrity: sha512-+ZbcxhB1C34RQR5IldSyY6Z/yalJIxHXhP36PGE4sEq0f/lgL5jCLmOKoDeVm8dqd9gFGDsd1bNWHSGCAwdSkw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.71.0': - resolution: {integrity: sha512-Hb1P5yKb9aiH2VSbm3Ml37mKjzqpunYet9krw/YK7V5JsAUJDQlju3Yk/dEqQvJD/usBvpi01Vbk9ZDNo0a8Cw==} + '@oxfmt/binding-freebsd-x64@0.72.0': + resolution: {integrity: sha512-yGPhA8sePEBFV9iGcprhyOk3Bz3fZshswMH3DpYIPZFLT1DG5LjiZ0j6AJQpSueaL6bUK0av1K+5+fe+ldy5EQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.71.0': - resolution: {integrity: sha512-r7n4JEO+rkp0kaP3ia+0hv6FzK3PQIHqaF0jxGuhjJ4JHVX9ZVRbgpDVDW+OloyuJsRWpWFDKIHe8gfl1p/4mQ==} + '@oxfmt/binding-linux-arm-gnueabihf@0.72.0': + resolution: {integrity: sha512-/g5axuqFMcAtoQrFLR5EJEMDjR5Ea4GuzpdDmMpPWAgrF1DmL4kD1NPNVlpXEOxpFJnD6lPvLKYj1lzdlrfifA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.71.0': - resolution: {integrity: sha512-EEynTF4nakgqDhVCmd3cHQCmTcw0BM8ea6eHigTdtZ3yxdsBNv+dYkt+OpDDZx1n/jzjI7J+Ht7IgkqFWuJUxQ==} + '@oxfmt/binding-linux-arm-musleabihf@0.72.0': + resolution: {integrity: sha512-InVitdHbS09PM+w+WinIiyX37rC9R3L7lUP9mKy9uKTEDSUFaLjWsLoreRIxKivnv7Ndqc6Trvi+K+b6ZzhiJg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.71.0': - resolution: {integrity: sha512-7VgJIrywCwR/G6YMv+HqsccUt8Z4q4mbM2xR+Ry/6AeMk4pCmZ9aO4Srd3mmrvVu8PsCZGP7oHEHSXWTmyloaQ==} + '@oxfmt/binding-linux-arm64-gnu@0.72.0': + resolution: {integrity: sha512-WKjlyRAxpPjXoqeuwFBim5LscgvY+RNEiZ971C9JlRQZONhJTsXDki/oHFYQE+fzzRpzXh3owZvOdT3kNL2g+g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.71.0': - resolution: {integrity: sha512-AOCaminv/+fhinUXKvtPZT4POhNYmN9GfvaJdgJa1AbvuzClWGwDIXfJg8eu3sYaVL2B68jNvmS7LZGAYa0z3A==} + '@oxfmt/binding-linux-arm64-musl@0.72.0': + resolution: {integrity: sha512-poafnbsPpJhR+5Mbwpf8CscugTyiRAzjTOxRVn8ra3X2jnURs2xL5Du2xl5g2iW85lICMglpmLf8jDjZEbNgzQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.71.0': - resolution: {integrity: sha512-0SHxuaa4QRLM7jfsd8wc6L7KdLg+FR6/ah9k1eAvcrK/Sni7oxchMvsjwUsyakVAta+wBVDkjJTr7/RFU0uUGA==} + '@oxfmt/binding-linux-ppc64-gnu@0.72.0': + resolution: {integrity: sha512-zNUi0gKRsO7S3IfPzsTbnm88OedPuX32hmZFWZRWHvWJlAoZRwVbeO6Nb3hng4vDgL7qUKp4TGqzPz1mhsycYg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.71.0': - resolution: {integrity: sha512-UNKj1+rPorTK6uSlQzpN96vDNbHbL/63zcpznweo0ImQdkDdtQCS7G3puxX++2cESPKT2B56Y6DICUmFTJde5A==} + '@oxfmt/binding-linux-riscv64-gnu@0.72.0': + resolution: {integrity: sha512-qTY7z/iyF/rWI2HWwWMevXqZW5PZUOofUy2afK7v6rseXnpld3yjgyIk6pCux3DqWQ0lB+f5atvLJNBwzLvFsw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.71.0': - resolution: {integrity: sha512-5cVXKj7a0mWu7GU6/bZgNyv+KtLgM5S9cnT26Os2/abj8pK/bklHsbdYEHGLLniGjNYkyzwem8mZciPCaDtilw==} + '@oxfmt/binding-linux-riscv64-musl@0.72.0': + resolution: {integrity: sha512-w18qfo8vEMD6EKEHtzTvjc6jH8yCyXIT1Bn7PFWJd0PpZyzH/VQDx4xvjCh0LGz6GRzqiezbisTdG9AoXtpkZQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.71.0': - resolution: {integrity: sha512-lNpcMuZvyU1V/CY5h19hac03m4E4PLSYRckcxZOynSPojI4Mwt6nEu9wT/gVf0bclH3xGEWjwqApnSTgjkd2oA==} + '@oxfmt/binding-linux-s390x-gnu@0.72.0': + resolution: {integrity: sha512-grW8HwEir+vK7fCcq1ZgL3NHQCfZ1PidiERE9Hbf+24xGys+DoDaoqTht4ccfn0PcBwbViLkwkda/KNpJu2KRQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.71.0': - resolution: {integrity: sha512-5/Z6pUewQpknXqC4/ykK6Zc6RiteAnPem1Ci7K1RZLVF6w6MMjwHjR4vsjijW4Czidgv7HKeVglGjElADliT9w==} + '@oxfmt/binding-linux-x64-gnu@0.72.0': + resolution: {integrity: sha512-wQJRQfWBRIn88Pkr449EUViwRayrYUScg7ty+Ihiem21mQ9BUA6S9e/4RAgyNk9lMag0KBouyKskjq61rOC0WQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.71.0': - resolution: {integrity: sha512-uVdG2N/4GEbOeljpQ+xv+NeEwJWJGj0WaxSiSYnoiqIYy3RWrWd3rGUmxWXP1A8+ferNvvwFoDAtvgsDUvBuSw==} + '@oxfmt/binding-linux-x64-musl@0.72.0': + resolution: {integrity: sha512-T1B0uQzafxuGPjjaPrZJ497nxoi1BiI65JfsYc56nNpmev3msfb/iaTmH2kQuNFqa8PKxpMlf3GhWrWBvBPORQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.71.0': - resolution: {integrity: sha512-1LwEZmSDRFKeBaGd/Hgj2xI3SmgGFpF4YtgkSx0sTYdTiZfHWbcFvRIBjL1YiDNdQd9Khda3Ij8CoqBULjskDg==} + '@oxfmt/binding-openharmony-arm64@0.72.0': + resolution: {integrity: sha512-OKxxgTurl7+hypVplO8QRAbbQlwEs0j1Y6FZv+ZN2qNbdxQk2muMlzuJNBGTCWwxQ0rkkOD1uK/DW1fvzoUSJA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.71.0': - resolution: {integrity: sha512-5tplmkeMXmza3PUptJQy25wCPJbIAg5g5r7zs+DI+7eEYOqbY+7roMdSH2zbDvv1Pb4oTDJ6NWYx9IRxBxXwhQ==} + '@oxfmt/binding-win32-arm64-msvc@0.72.0': + resolution: {integrity: sha512-L78TKzURqvjuZOCo88ixMbPXf/RP7u31Y0cmjJ/KEmrHmfYhmhEvsDaTYPiyTRne64jUgCbADfY0YeoQk4/Xyw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.71.0': - resolution: {integrity: sha512-T2LWgh3Vx4PYDjtcqRRGDyCsVxeF/VE5xTpUQiCm65bNLwMpRnJYj6Hlkf6qRM3+jxCIOqHD1kVHNQCCiAVMNQ==} + '@oxfmt/binding-win32-ia32-msvc@0.72.0': + resolution: {integrity: sha512-gQtFW+Ii8BWgtJTAD17TY/WsdNS5gUfAPov8iI1jeq4r9lRRYImmxPL08UU00/RLaCfBJwh2UgINnDY0iOiZ+A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.71.0': - resolution: {integrity: sha512-L06SVp7Hu1EQTkh3+PHnKK26dxVbEpOG2InMwCooTE4MA437b5FgfTleCCiGn4ykISHnHU6UHaIg7SiMO7rBHA==} + '@oxfmt/binding-win32-x64-msvc@0.72.0': + resolution: {integrity: sha512-uhooIN+DCzWX5uH7mQkSVCsODqsGluGJ4n3ch4+mITtWKpESMDjUJUSDBAVFuYQDwqY2McPDKdKJbM0oZVW/Qw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -363,6 +366,12 @@ packages: '@types/node@24.13.5': resolution: {integrity: sha512-TXyindR+lBr22aJIdMQzCFHPHR6cR4js838mRDCSz5hOKWZvZwsXSSiXDmjRj4iJmgl+sR9O+1mkoVBSMadNug==} + '@types/whatwg-mimetype@3.0.2': + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} + + '@types/ws@8.18.2': + resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==} + '@typescript/typescript-aix-ppc64@7.0.2': resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} engines: {node: '>=16.20.0'} @@ -501,6 +510,10 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + buffer-image-size@0.6.4: + resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} + engines: {node: '>=4.0'} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -518,6 +531,10 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} @@ -542,6 +559,10 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + happy-dom@20.14.6: + resolution: {integrity: sha512-5DDsf4QmEIlRL6NkCv/fxSjYHHkZceL/IaYQdJc2h3G7DRTEai++VTjbfL0LB9geW2VrA3+vrjF3Efx+OTtX0Q==} + engines: {node: '>=20.0.0'} + immutable@5.1.9: resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} @@ -642,8 +663,8 @@ packages: resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} - oxfmt@0.71.0: - resolution: {integrity: sha512-lUPUl0d/+Io5pDrsPXWs6rB4N/bpB78oj9CTDpnbulfDz+0r3XXcHPlQ7kRPJ2GjIT4nX+/mcqunOeP9BvsEtg==} + oxfmt@0.72.0: + resolution: {integrity: sha512-16OQeL0uhPZbfmT9S9WWM0Nv9+BmVi8Ra1uTYaSKj/qSCs6FB30KOhz9JoNYw6thT+K5bVxsk/tSEOu497i34g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -679,6 +700,10 @@ packages: resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.29: + resolution: {integrity: sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==} + engines: {node: ^10 || ^12 || >=14} + readdirp@5.1.1: resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} engines: {node: '>= 20.19.0'} @@ -702,6 +727,10 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} + std-env@4.2.0: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} @@ -816,11 +845,27 @@ packages: jsdom: optional: true + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + why-is-node-running@3.2.1: resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} engines: {node: '>=20.11'} hasBin: true + ws@8.22.0: + resolution: {integrity: sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + snapshots: '@jridgewell/resolve-uri@3.1.2': {} @@ -834,61 +879,61 @@ snapshots: '@oxc-project/types@0.150.0': {} - '@oxfmt/binding-android-arm-eabi@0.71.0': + '@oxfmt/binding-android-arm-eabi@0.72.0': optional: true - '@oxfmt/binding-android-arm64@0.71.0': + '@oxfmt/binding-android-arm64@0.72.0': optional: true - '@oxfmt/binding-darwin-arm64@0.71.0': + '@oxfmt/binding-darwin-arm64@0.72.0': optional: true - '@oxfmt/binding-darwin-x64@0.71.0': + '@oxfmt/binding-darwin-x64@0.72.0': optional: true - '@oxfmt/binding-freebsd-x64@0.71.0': + '@oxfmt/binding-freebsd-x64@0.72.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.71.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.72.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.71.0': + '@oxfmt/binding-linux-arm-musleabihf@0.72.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.71.0': + '@oxfmt/binding-linux-arm64-gnu@0.72.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.71.0': + '@oxfmt/binding-linux-arm64-musl@0.72.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.71.0': + '@oxfmt/binding-linux-ppc64-gnu@0.72.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.71.0': + '@oxfmt/binding-linux-riscv64-gnu@0.72.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.71.0': + '@oxfmt/binding-linux-riscv64-musl@0.72.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.71.0': + '@oxfmt/binding-linux-s390x-gnu@0.72.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.71.0': + '@oxfmt/binding-linux-x64-gnu@0.72.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.71.0': + '@oxfmt/binding-linux-x64-musl@0.72.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.71.0': + '@oxfmt/binding-openharmony-arm64@0.72.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.71.0': + '@oxfmt/binding-win32-arm64-msvc@0.72.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.71.0': + '@oxfmt/binding-win32-ia32-msvc@0.72.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.71.0': + '@oxfmt/binding-win32-x64-msvc@0.72.0': optional: true '@parcel/watcher-android-arm64@2.6.0': @@ -1008,6 +1053,12 @@ snapshots: dependencies: undici-types: 7.18.2 + '@types/whatwg-mimetype@3.0.2': {} + + '@types/ws@8.18.2': + dependencies: + '@types/node': 24.13.5 + '@typescript/typescript-aix-ppc64@7.0.2': optional: true @@ -1081,6 +1132,10 @@ snapshots: assertion-error@2.0.1: {} + buffer-image-size@0.6.4: + dependencies: + '@types/node': 24.13.5 + chai@6.2.2: {} chokidar@5.0.0: @@ -1091,6 +1146,8 @@ snapshots: detect-libc@2.1.2: {} + entities@7.0.1: {} + es-module-lexer@2.3.2: {} estree-walker@3.0.3: @@ -1106,6 +1163,19 @@ snapshots: fsevents@2.3.3: optional: true + happy-dom@20.14.6: + dependencies: + '@types/node': 24.13.5 + '@types/whatwg-mimetype': 3.0.2 + '@types/ws': 8.18.2 + buffer-image-size: 0.6.4 + entities: 7.0.1 + whatwg-mimetype: 3.0.0 + ws: 8.22.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + immutable@5.1.9: {} is-extglob@2.1.1: @@ -1176,37 +1246,37 @@ snapshots: obug@2.2.1: {} - oxfmt@0.71.0: + oxfmt@0.72.0: dependencies: tinypool: 2.2.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.71.0 - '@oxfmt/binding-android-arm64': 0.71.0 - '@oxfmt/binding-darwin-arm64': 0.71.0 - '@oxfmt/binding-darwin-x64': 0.71.0 - '@oxfmt/binding-freebsd-x64': 0.71.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.71.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.71.0 - '@oxfmt/binding-linux-arm64-gnu': 0.71.0 - '@oxfmt/binding-linux-arm64-musl': 0.71.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.71.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.71.0 - '@oxfmt/binding-linux-riscv64-musl': 0.71.0 - '@oxfmt/binding-linux-s390x-gnu': 0.71.0 - '@oxfmt/binding-linux-x64-gnu': 0.71.0 - '@oxfmt/binding-linux-x64-musl': 0.71.0 - '@oxfmt/binding-openharmony-arm64': 0.71.0 - '@oxfmt/binding-win32-arm64-msvc': 0.71.0 - '@oxfmt/binding-win32-ia32-msvc': 0.71.0 - '@oxfmt/binding-win32-x64-msvc': 0.71.0 + '@oxfmt/binding-android-arm-eabi': 0.72.0 + '@oxfmt/binding-android-arm64': 0.72.0 + '@oxfmt/binding-darwin-arm64': 0.72.0 + '@oxfmt/binding-darwin-x64': 0.72.0 + '@oxfmt/binding-freebsd-x64': 0.72.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.72.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.72.0 + '@oxfmt/binding-linux-arm64-gnu': 0.72.0 + '@oxfmt/binding-linux-arm64-musl': 0.72.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.72.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.72.0 + '@oxfmt/binding-linux-riscv64-musl': 0.72.0 + '@oxfmt/binding-linux-s390x-gnu': 0.72.0 + '@oxfmt/binding-linux-x64-gnu': 0.72.0 + '@oxfmt/binding-linux-x64-musl': 0.72.0 + '@oxfmt/binding-openharmony-arm64': 0.72.0 + '@oxfmt/binding-win32-arm64-msvc': 0.72.0 + '@oxfmt/binding-win32-ia32-msvc': 0.72.0 + '@oxfmt/binding-win32-x64-msvc': 0.72.0 picocolors@1.1.1: {} picomatch@4.0.7: {} - postcss-combine-duplicated-selectors@11.0.0(postcss@8.5.28): + postcss-combine-duplicated-selectors@11.0.0(postcss@8.5.29): dependencies: - postcss: 8.5.28 + postcss: 8.5.29 postcss-selector-parser: 7.1.6 postcss-value-parser: 4.2.0 @@ -1223,6 +1293,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.29: + dependencies: + nanoid: 3.3.19 + picocolors: 1.1.1 + source-map-js: 1.2.2 + readdirp@5.1.1: {} rolldown@1.2.9: @@ -1258,6 +1334,8 @@ snapshots: source-map-js@1.2.1: {} + source-map-js@1.2.2: {} + std-env@4.2.0: {} tinybench@6.1.4: {} @@ -1310,7 +1388,7 @@ snapshots: fsevents: 2.3.3 sass: 1.105.1 - vitest@5.0.3(@types/node@24.13.5)(vite@8.3.0(@types/node@24.13.5)(sass@1.105.1)): + vitest@5.0.3(@types/node@24.13.5)(happy-dom@20.14.6)(vite@8.3.0(@types/node@24.13.5)(sass@1.105.1)): dependencies: '@types/chai': 5.2.3 '@vitest/mocker': 5.0.3(vite@8.3.0(@types/node@24.13.5)(sass@1.105.1)) @@ -1328,7 +1406,12 @@ snapshots: why-is-node-running: 3.2.1 optionalDependencies: '@types/node': 24.13.5 + happy-dom: 20.14.6 transitivePeerDependencies: - msw + whatwg-mimetype@3.0.0: {} + why-is-node-running@3.2.1: {} + + ws@8.22.0: {} diff --git a/test/docs.test.ts b/test/docs.test.ts new file mode 100644 index 0000000..fd10d5f --- /dev/null +++ b/test/docs.test.ts @@ -0,0 +1,53 @@ +import fs from 'node:fs'; +import { Window } from 'happy-dom'; +import { describe, expect, test } from 'vitest'; + +import { localPath, RMD_ALL_STYLES } from './shared'; + +describe('docs', () => { + test('styles page lists all styles', () => { + const document = getDocument('styles'); + + const h1 = document.querySelector('h1'); + expect(h1).not.toBe(null); + expect(h1!.textContent).toMatch('Remarkdown styles'); + + const toc = document.querySelector('#styles-list'); + expect(toc).not.toBe(null); + + const list = [...toc!.querySelectorAll('tbody td:first-child')] + .map((el) => el.textContent) + .toSorted(); + expect(list).toEqual(RMD_ALL_STYLES); + }); + + test('styles page toc links to existing elements', () => { + const document = getDocument('styles'); + + const toc = document.querySelector('#styles-list'); + expect(toc).not.toBe(null); + const links = [...toc!.querySelectorAll('tbody td:first-child a')]; + expect(links.length).toBeGreaterThanOrEqual(RMD_ALL_STYLES.length); + + for (const link of links) { + const href = link.getAttribute('href') ?? ''; + expect(href).toMatch(/^#[a-z-]+$/); + const target = document.querySelector(href); + expect(target).not.toBe(null); + expect(target!.tagName).toBeOneOf(['A', 'H2', 'H3']); + } + }); +}); + +type DocPage = 'index' | 'styles' | 'config'; + +function getDocument(page: DocPage) { + const html = getHtml(page); + const url = `http://localhost:8080/${page === 'index' ? '' : page}`; + const window = new Window({ url }); + return new window.DOMParser().parseFromString(html, 'text/html'); +} + +function getHtml(page: DocPage) { + return fs.readFileSync(localPath(`docs/${page}.html`), { encoding: 'utf8' }); +}