Generate html by writing css.
npm i html-by-cssimport generate from 'html-by-css';
const source = `
ul#list {
list-style: none;
margin: 0;
padding: 0;
& li.item*3 {
padding: .5rem;
:is(a[href="#"]) {
content: Link;
color: inherit;
}
}
}
`;
const { html, css } = generate(source);HTML
<ul id="list">
<li class="item">
<a href="#">Link</a>
</li>
<li class="item">
<a href="#">Link</a>
</li>
<li class="item">
<a href="#">Link</a>
</li>
</ul>CSS
/* legacy: false (default) */
ul#list {
list-style: none;
margin: 0;
padding: 0;
& li.item {
padding: .5rem;
:is(a[href="#"]) {
color: inherit;
}
}
}
/* legacy: true */
ul#list {
list-style: none;
margin: 0;
padding: 0;
}
ul#list li.item {
padding: .5rem;
}
ul#list li.item :is(a[href="#"]) {
color: inherit;
}- Use
postcssto parse and walk through source. - On target nodes, process data as HTML:
- Find
contenton non-pseudo elements and inject as text. - Find
emmetsyntax and duplicate elements. - Parse selector using
css-what. - Transform selector to
himalayaschema.
- Find
- Process source as valid CSS:
- Remove
contenton non-pseudo elements. - Remove
emmetsyntax. - Optionally, use
postcss-nestingplugin to transform into legacy, non-nested CSS. - Apply additional plugins as provided.
- Remove
- Return object with
{ html, css }.
The nesting should be prepared using the current w3 CSS Nesting specification. The most important concept is that a nested selector must start with a symbol.
.foo {
/* ❌ invalid */
span {
color: hotpink;
}
/* ✅ valid */
& span {
color: hotpink;
}
/* ❌ invalid */
span & {
color: hotpink;
}
/* ✅ valid */
:is(span) & {
color: hotpink;
}
} If you wish to collapse the nesting for the CSS output, set legacy: true in the options. This uses postcss-nesting with the default options.
const { html, css } = generate(source, { legacy: true });If you want to set your own options, provide your own version of the postcss-nesting plugin and configure.
import nesting from 'postcss-nesting';
import generate from 'html-by-css';
const postcssPlugins = [nesting({
noIsPseudoSelector: true
})];
const { html, css } = generate(source, { plugins: postcssPlugins });Warning
Do not use
legacy: truewith your ownpostcss-nestingconfiguration. The internal (legacy) usage will run first. Either do not declare or explicitly setlegacy: false. This is only when using a custompostcss-nesting, all other plugins can be used withlegacy: true.
You can include additional postcss plugins. Example below helps with removing duplicate declarations.
import dedupe from 'postcss-discard-duplicates';
import generate from 'html-by-css';
const postcssPlugins = [dedupe()];
const { html, css } = generate(source, { plugins: postcssPlugins });This is helpful if you have several similar elements with different contents.
ul#list {
& li.item {
& a*0 {
color: inherit;
}
& a[href="/home"] {
content: Home;
}
& a[href="/about"] {
content: About;
}
& a[href="/contact"] {
content: Contact
}
}
}Note
The use of
a*0says write the styles found here, but don't write HTML. When you use this, the represented node and it's children will not be written as HTML.
To create multiple elements, use emmet syntax.
ul {
li*5 {
/* Makes 5 <li/> elements */
}
}This li*5 selector is not valid CSS and is transformed during processing to li.
To add text content, use the content property on non-pseudo elements.
main {
& h1 {
content: Hello world!;
}
}<main>
<h1>Hello world!</h1>
</main>Note
There are no quotes around the string. Adding quotes would include the quotes in the output.
The content property is not valid on non-pseudo elements and is removed from these declarations during processing.
npm t- Using
cheerioto traverse HTML in tests. - Using
@projectwallace/css-analyzerto analyze returned CSS.
There's definitely some cases not covered in the tests yet.
-
contentprop and nested selector (both text and children). - Other pseudo-selectors (
:nth-child(),:checked).
Your scientists were so preoccupied with whether they could, they didn't stop to think if they should.
There's a few projects out there that are HTML preprocessors (Haml, Pug) which have their own (sometimes CSS-like) syntax. I wondered if we could get closer to just writing CSS to produce HTML. With the new nesting specification and the power of postcss, it looks like we can!