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

quires

v1.0.0

Published

## Classy web documents with Svelte and Djot

Downloads

61

Readme

Quires

Classy web documents with Svelte and Djot

Quires is a system for creating reactive and interactive documents from Markdown, built on top of Svelte and djot, the new parser from the creator of Pandoc, John MacFarlane These documents are "classy" because they use HTML syntax overriding elements based on class and other HTML attributes; you can set codeblocks of one class to do certain types of things.

Because Svelte and Svelte-kit make it easy to generate static web pages, you can use Quires to build classic static sites just like something that compiles markdown to HTML; out of the box, this is basically all it does! But by overriding components, you can add all kinds of useful web interactions on top of your documents.

Quires also includes a webpack parser for djot. Putting markdown into your files is easy:

<script>
  import doc from 'myfile.md'`;
  import { Document} from 'quires';
</script>

<Document quire={doc}>

Overview

You write documents in pandoc-flavored Markdown (or in raw djot, which is almost the same thing). If your document does not include any reactive elements, you use the base set of components that comes with the system and get the same behavior as an ordinary Markdown parser. The Svelte compiler boils it straight into raw HTML. But if you want to change the way an element renders, you can generate a custom svelte component that folds in new behavior.

For instance, in the preceding paragraph I said djot is "almost the same thing" as markdown. But there's one really important difference: in djot markup, a string *enclosed in asterisks* is rendered as bold. Whereas in markdown, this is rendered in italics, and **double asterisks** are required to render bold. To switch to the markdown behavior, we write a custom component that checks to see if a <strong> element is double-nested: if so, we render it as strong; otherwise as an emph.

<!-- src/lib/MarkdownStrong.svelte -->
<script lang="ts">
	import BasicStrong from '$lib/Inlines/Strong.svelte';
	import BasicEmph from '$lib/Inlines/Emph.svelte';
	import type { Strong, Emph } from '@djot/djot';

	let { quire }: { quire: Quire<Strong> } = $props();

	let dtype = $derived(
		quire.content.children.length === 1 && quire.content.children[0].tag === 'strong'
			? 'strong'
			: 'emph'
	);
</script>

{#if dtype === 'strong'}
	<BasicStrong quire={{ ...quire, content: quire.content.children[0] as Strong }} />
{:else}
	<BasicEmph quire={{ ...quire, content: { ...quire.content, tag: 'emph' } }} />
{/if}

To implement this, we define the quire at the top level

<script lang="ts">
	// src/routes/+page.svelte
	import quire from './index.md';
	import Doc from '$lib/Doc.svelte';
	import Strong from '$lib/MyStrong.svelte';
	// The quire contains both the document
	quire.quireComponents = [[{ tag: 'strong', selector: 'strong', component: Strong }]];
</script>

<Doc {quire} />

The quire contains both the AST and a set of state that is drilled down to its children. These can be disassembled and passed to components lower down the tree, but Quire components should -- as a general rule -- pass down all state to children that they don't explicitly alter.

Contingent quires

You can pass quires that only override some versions of components. For this quire uses a DSL based on css selectors, parsed by the css-what repo.

Most css selectors are not (yet?) supported, but class based ones are.

  • code_block: Replaces 'code_block' elements with the custom component.
  • code_block.python: Replaces 'code_block' elements with the language as python with the custom component. Note:
    • the language of a code block is treated as a class for the purpose of selectors.
  • para#intro: Replaces paragraphs with the id "#intro"

These behaviors can be combined.

  • para.executable code_block.js: Encloses all js codeblocks that are descendants of a para element classed executable.

Wrapping default behavior

Often you want to do something relatively simple, like put a button below a code block, without altering the basic behavior of the code block itself.

This is handled using the <slot /> property of the svelte component.

For example, take following component:

<div style="color:red;">
	<slot></slot>
</div>

This component wraps its child with a red-colored div. It can then be used as a wrapper around multiple different components, if desired.

<script lang="ts">
	import index from './index.md';
	import { Document } from 'quires/';
	import { document } from '$lib/quire';
	import Warning from 'RedWrapper.svelte';
	// The quire contains both the document and the settings.
	const quire = document(index, { 'div.warning': Warning, 'para.warning': Warning });
</script>

<h1>{index.metadata.title}</h1>

<Doc {quire} />

Classes are (for the time being) not inherited. This behavior is likely to change.