npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@chearon/overflow

v0.0.0-alpha.1

Published

A small CSS2 document renderer built from specifications for learning purposes

Downloads

7

Readme

overflow

Overflow is a CSS layout engine created to explore the reaches of the foundational CSS standards (that is: inlines, blocks, floats, positioning and eventually tables, but not flexbox or grid). It has a high quality text layout implementation and is capable of displaying many of the languages of the world. You can use it to generate PDFs or images on the backend with Node and node-canvas or render rich, wrapped text to a canvas in the browser.

Features

  • Bidirectional and RTL text
  • Hyperscript (h()) API with styles as objects in addition to accepting HTML and CSS
  • Any OpenType/TrueType buffer can (and must) be registered
  • Font fallbacks at the grapheme level
  • Colored diacritics
  • Desirable line breaking (e.g. carries starting padding to the next line)
  • Optimized shaping
  • Inherited and cascaded styles are never calculated twice
  • Handles as many CSS layout edge cases as I can find
  • Fully typed
  • Lots of tests
  • Fast

Usage

Overflow works off of a DOM with inherited and calculated styles, the same way that browsers do. You create the DOM with the familiar h() function, and specify styles as plain objects.

import {h, renderToCanvas, registerFont} from 'overflow';
import {createCanvas} from 'canvas';
import fs from 'node:fs';

// Register fonts before layout. This is a required step.
// It is only async when you don't pass an ArrayBuffer
await registerFont(new URL('fonts/Roboto-Regular.ttf', import.meta.url));
await registerFont(new URL('fonts/Roboto-Bold.ttf', import.meta.url));

// Always create styles at the top-level of your module if you can
const divStyle = {
  backgroundColor: {r: 28, g: 10, b: 0, a: 1},
  color: {r: 179, g: 200, b: 144, a: 1},
  textAlign: 'center'
};

// Since we're creating styles directly, colors have to be defined numerically
const spanStyle = {
  color: {r: 115, g: 169, b: 173, a: 1},
  fontWeight: 700
};

// Create a DOM
const rootElement = h('div', {style: divStyle}, [
  'Hello, ',
  h('span', {style: spanStyle}, ['World!'])
]);

// Layout and paint into the entire canvas (see also renderToCanvasContext)
const canvas = createCanvas(250, 50);
renderToCanvas(rootElement, canvas, /* optional density: */ 2);

// Save your image
canvas.createPNGStream().pipe(fs.createWriteStream(new URL('hello.png', import.meta.url)));

Hello world against a dark background, with "world" bolded and colored differently

HTML

This API is only recommended if performance is not a concern, or for learning purposes. Parsing adds extra time (though it is fast thanks to @fb55) and increases bundle size significantly.

import {parse, renderToCanvas, registerFont} from 'overflow/with-parse.js';
import {createCanvas} from 'canvas';
import fs from 'node:fs';

await registerFont(new URL('fonts/Roboto-Regular.ttf', import.meta.url));
await registerFont(new URL('fonts/Roboto-Bold.ttf', import.meta.url));

const rootElement = parse(`
  <div style="background-color: #1c0a00; color: #b3c890; text-align: center;">
    Hello, <span style="color: #73a9ad; font-weight: bold;">World!</span>
  </div>
`);

const canvas = createCanvas(250, 50);
renderToCanvas(rootElement, canvas, 2);

canvas.createPNGStream().pipe(fs.createWriteStream(new URL('hello.png', import.meta.url)));

Performance characteristics

Performance is a top goal and is second only to correctness. Run the performance examples in the examples directory to see the numbers for yourself.

  • 8 paragraphs with several inline spans of different fonts can be turned from HTML to image in 7ms on a 2019 MacBook Pro and 16ms on a 2012 MacBook Pro (perf-1.ts)
  • The Little Prince (over 500 paragraphs) can be turned from HTML to image in under 150ms on a 2019 MacBook Pro and under 300ms on a 2012 MacBook Pro (perf-2.ts)
  • A 10-letter word can be generated and laid out (not painted) in under 25µs on a 2019 MacBook Pro and under 80µs on a 2012 MacBook Pro (perf-3.ts)

Shaping is done with harfbuzz. Harfbuzz compiled to WebAssembly can achieve performance metrics similar to CanvasRenderingContext2D's measureText, but it is not as fast. A smart implementation of text layout in Javascript that uses measureText (such as using a word cache, which is what GSuite apps do) will still be faster than overflow, but not significantly so, and with drawbacks (for example, fonts with effects across spaces won't work and colored diacritics are not possible).

The fastest performance can be achieved by using the hyperscript API, which creates a DOM directly and skips the typical HTML and CSS parsing steps. Take care to re-use style objects to get the most benefits. Reflows at different widths are faster than recreating the layout tree.

Supported CSS rules

Following are rules that work or will work soon. Shorthand properties are not listed. If you see all components of a shorthand (for example, border-style, border-width, border-color) then the shorthand is assumed to be supported (for example border).

Inline formatting

| Property | Values | Status | | -- | -- | -- | | color | rgba(), rgb(), #rrggbb, #rgb, #rgba | ✅‍ Works | | direction | ltr, rtl | ✅‍ Works | | font-‍family | | ✅‍ Works | | font-‍size | em, px, smaller etc, small etc, cm etc | ✅‍ Works | | font-‍stretch | condensed etc | ✅‍ Works | | font-‍style | normal, italic, oblique | ✅‍ Works | | font-‍variant | | 🚧‍ Planned | | font-‍weight | normal, bolder, lighter light, bold, 100-900 | ✅‍ Works | | line-‍height | normal, px, em, %, number | ✅‍ Works | | tab-‍size | | 🚧‍ Planned | | text-‍align | start, end, left, right, center | ✅‍ Works | | text-‍decoration | | 🚧‍ Planned | | unicode-‍bidi | | 🚧‍ Planned | | vertical-‍align | baseline, middle, sub, super, text-top, text-bottom, %, px etc, top, bottom | ✅‍ Works | | white-‍space | normal, nowrap, pre, pre-wrap, pre-line | ✅‍ Works |

Block formatting

| Property | Values | Status | | -- | -- | -- | | clear | left, right, both, none | ✅‍ Works | | float | left, right, none | ✅‍ Works | | writing-‍mode | horizontal-tb, vertical-lr, vertical-rl | 🏗 Partially done1 |

1Implemented for BFCs but not IFCs yet

Boxes and positioning

| Property | Values | Status | | -- | -- | -- | | background-‍clip | border-box, content-box, padding-box | ✅‍ Works | | background-‍color | rgba(), rgb(), #rrggbb, #rgb, #rgba | ✅‍ Works | | border-‍color | rgba(), rgb(), #rrggbb, #rgb, #rgba | ✅‍ Works | | border-‍style | solid, none | ✅‍ Works | | border-‍width | em, px, cm etc | ✅‍ Works | | bottom | | 🚧‍ Planned | | box-‍sizing | border-box, content-box | ✅‍ Works | | display | block, inline, flow-root, none | ✅‍ Works | | display | inline-block, table | 🚧‍ Planned | | | height | em, px, %, cm etc, auto | ✅‍ Works | | left | | 🚧‍ Planned | | margin | em, px, %, cm etc, auto | ✅‍ Works | | padding | em, px, %, cm etc | ✅‍ Works | | position | absolute | 🚧‍ Planned | | position | fixed | 👎‍ No interest1 | | position | relative | 🚧‍ Planned | | right | | 🚧‍ Planned | | top | | 🚧‍ Planned | | overflow | | 🚧‍ Planned | | width | em, px, %, cm etc, auto | ✅‍ Works | | z-index | | 🚧‍ Planned |

1Any document that uses position: fixed could be reorganized and updated to use position: absolute and look identical. For that reason, I don't find fixed positioning very interesting.

Shout-outs

overflow doesn't have any package.json dependencies, but the work of many others made it possible. Javascript dependencies have been checked in and modified to varying degrees to fit this project, maintain focus, and rebel against dependency-of-dependency madness. Here are the projects I'm grateful for: