Blogging

ShareLinks

Links that share a post on X, Bluesky, Mastodon, LinkedIn, Reddit, Hacker News and more, or by email: plain links, nothing loaded from the networks.

#blogging#social#share#links#no-js

Demo

Preview

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/share_links.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/share_links.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/share_links.qwe
view ShareLinksDemo() =    <div.demo-stack>        <ShareLinks id="share-links-demo" url="https://blog.example.com/islands-resumed" title="Islands, resumed: how a page wakes up"                    via="quillolang"/>        <ShareLinks id="share-links-demo-labels" url="https://blog.example.com/islands-resumed" title="Islands, resumed"                    text="Worth a read, on resuming pages:" networks={["mastodon", "bluesky", "hackernews", "whatsapp", "telegram", "email"]}                    mastodon="hachyderm.io" show_labels={true} variant="soft" size="sm" label="Share"/>        <ShareLinks id="share-links-demo-side" url="https://blog.example.com/islands-resumed" title="Islands, resumed" label=""                    variant="ghost" layout="column" networks={["x", "linkedin", "email"]}/>    </div>

Source

ui/share_links.qwe
// ShareLinks: a row of links that share a post on X, Bluesky, Mastodon, LinkedIn,// Facebook, Reddit, Hacker News, WhatsApp, Telegram or by email: each opens that site's// own share page (in a new tab) with the post's address and title filled in. Plain// links: no script, no buttons from the networks, nothing loaded from them.////   <ShareLinks url="https://blog.example.com/islands" title="Islands, resumed"/>//   <ShareLinks url={post_url} title={post.title} text="Worth a read:" via="quillolang"//               networks={["mastodon", "bluesky", "hackernews", "email"]} mastodon="hachyderm.io" show_labels={true}/>//// url: the post's address, in full (a view cannot know the page's own). title: the// post's title (the subject of an email, the title on Reddit and Hacker News). text:// what a post on X, Bluesky, Mastodon, WhatsApp or Telegram says before the link (the// title). via: an X account to credit, without the @. networks: which, in order, from// "x", "bluesky", "mastodon", "linkedin", "facebook", "reddit", "hackernews", "whatsapp",// "telegram" and "email" (["x", "bluesky", "linkedin", "facebook", "reddit",// "hackernews", "email"]). mastodon: the server to share through ("mastodon.social").// label: the row's heading, and its name for screen readers ("Share this post"; "" shows// none, and the name stays). show_labels: the network's name beside each icon (false:// icons only). Screen readers hear "Share on X (opens in a new tab)" either way. size:// "sm", "md" (default) or "lg". variant: "outline" (default), "soft" or "ghost". layout:// "row" (default) or "column" (down the side of a post). id: the element's id.import ui/theme.qwe use ui_clslet share_links_css = css`.ui-bshare { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 0; font: 14px/1 var(--ui-font); color: var(--ui-fg); --s: 38px; }.ui-bshare-sm { --s: 32px; } .ui-bshare-lg { --s: 46px; }.ui-bshare-label { margin: 0; color: var(--ui-muted); font-size: 13px; font-weight: 600; }.ui-bshare-list { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }.ui-bshare-list a { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-width: var(--s); height: var(--s); padding: 0;  border: 1px solid var(--ui-border); border-radius: 999px; background: var(--ui-surface); color: var(--ui-fg); text-decoration: none;  transition: color .15s, background .15s, border-color .15s; }.ui-bshare-list a:hover { border-color: var(--ui-accent); color: var(--ui-accent); }.ui-bshare-list a:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-bshare-list svg { flex: none; width: calc(var(--s) * .45); height: calc(var(--s) * .45); }.ui-bshare-soft .ui-bshare-list a { border-color: transparent; background: var(--ui-subtle); }.ui-bshare-soft .ui-bshare-list a:hover { background: var(--ui-accent-soft); }.ui-bshare-ghost .ui-bshare-list a { border-color: transparent; background: none; color: var(--ui-muted); }.ui-bshare-ghost .ui-bshare-list a:hover { background: var(--ui-accent-soft); color: var(--ui-accent); }.ui-bshare-labels .ui-bshare-list a { padding: 0 14px 0 11px; font-weight: 500; }.ui-bshare-column { flex-direction: column; align-items: flex-start; }.ui-bshare-column .ui-bshare-list { flex-direction: column; }@media (prefers-reduced-motion: reduce) { .ui-bshare-list a { transition: none; } }`let share_links_default = ["x", "bluesky", "linkedin", "facebook", "reddit", "hackernews", "email"]let share_links_all = ["x", "bluesky", "mastodon", "linkedin", "facebook", "reddit", "hackernews", "whatsapp", "telegram", "email"]// The network's name.fn share_links_name(kind: str) -> str =    match kind {        "x" => "X"        "bluesky" => "Bluesky"        "mastodon" => "Mastodon"        "linkedin" => "LinkedIn"        "facebook" => "Facebook"        "reddit" => "Reddit"        "hackernews" => "Hacker News"        "whatsapp" => "WhatsApp"        "telegram" => "Telegram"        _ => "Email"    }// The address of the network's share page for a post.fn share_links_href(kind: str, url: str, title: str, text: str, via: str?, mastodon: str) -> str {    let said = url_encode(`${text} ${url}`)    match kind {        "x" => "https://x.com/intent/post?" + url_encode({ text, url, via })        "bluesky" => "https://bsky.app/intent/compose?text=" + said        "mastodon" => `https://${mastodon}/share?text=${said}`        "linkedin" => "https://www.linkedin.com/sharing/share-offsite/?url=" + url_encode(url)        "facebook" => "https://www.facebook.com/sharer/sharer.php?u=" + url_encode(url)        "reddit" => "https://www.reddit.com/submit?" + url_encode({ url, title })        "hackernews" => "https://news.ycombinator.com/submitlink?" + url_encode({ u: url, t: title })        "whatsapp" => "https://wa.me/?text=" + said        "telegram" => "https://t.me/share/url?" + url_encode({ url, text })        _ => "mailto:?" + url_encode({ subject: title, body: `${text}\n\n${url}` })    }}// The network's icon, on a 24 by 24 grid in currentColor.fn share_links_icon(kind: str) {    let filled = (d: str) => <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d={d}/></svg>    let lined = (body) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"                              stroke-linejoin="round" aria-hidden="true">{body}</svg>    match kind {        "x" => filled("M18.2 2h3.4l-7.4 8.5L23 22h-6.8l-5.3-7-6.1 7H1.4l7.9-9.1L1 2h7l4.8 6.4L18.2 2zm-1.2 18h1.9L7.1 3.9H5.1L17 20z")        "bluesky" => filled("M12 10.8c-1.1-2.1-4-6-6.8-8C2.6 1 1.6 1.3.9 1.6.2 2 0 3.1 0 3.8c0 .7.4 5.6.6 6.4.8 2.7 3.7 3.6 6.4 3.3-3.9.6-7.4 2-2.8 7 5 5.2 6.9-1.1 7.8-4.3.9 3.2 2 9.3 7.7 4.3 4.3-4.3 1.2-6.4-2.7-7 2.7.3 5.6-.6 6.4-3.3.2-.8.6-5.7.6-6.4 0-.7-.2-1.8-.9-2.2-.7-.3-1.7-.6-4.3 1.2-2.8 2-5.7 5.9-6.8 8z")        "mastodon" => filled("M21.3 13.6c-.3 1.5-2.6 3.1-5.2 3.4-1.4.2-2.7.3-4.1.2-2.3-.1-4.1-.5-4.1-.5v.6c.3 2.3 2.3 2.4 4.2 2.5 1.9.1 3.6-.5 3.6-.5l.1 1.7s-1.3.7-3.7.9c-1.3.1-2.9-.1-4.8-.5C3.2 20.4 2.5 16 2.4 11.6V8.1c0-4.5 3-5.8 3-5.8C6.9 1.6 9.4 1.3 12 1.3h.1c2.6 0 5.1.3 6.6 1 0 0 3 1.3 3 5.8 0 0 0 3.3-.4 5.5zm-3.1-5.2v5.5h-2.2V8.6c0-1.1-.5-1.7-1.4-1.7-1 0-1.6.7-1.6 2v2.9h-2.2V8.9c0-1.3-.5-2-1.6-2-1 0-1.4.6-1.4 1.7v5.3H5.6V8.4c0-1.1.3-2 .9-2.6.6-.7 1.4-1 2.4-1 1.1 0 2 .4 2.6 1.3l.5.9.6-.9c.6-.9 1.5-1.3 2.6-1.3 1 0 1.8.3 2.4 1 .6.6.9 1.5.9 2.6z")        "linkedin" => filled("M20.4 2H3.6A1.6 1.6 0 0 0 2 3.6v16.8A1.6 1.6 0 0 0 3.6 22h16.8a1.6 1.6 0 0 0 1.6-1.6V3.6A1.6 1.6 0 0 0 20.4 2zM8 19H5V9.5h3V19zM6.5 8.2a1.7 1.7 0 1 1 0-3.4 1.7 1.7 0 0 1 0 3.4zM19 19h-3v-4.6c0-1.1 0-2.5-1.5-2.5s-1.8 1.2-1.8 2.4V19h-3V9.5h2.9v1.3h.1c.4-.8 1.4-1.5 2.8-1.5 3 0 3.5 2 3.5 4.5V19z")        "facebook" => filled("M24 12a12 12 0 1 0-13.9 11.9v-8.4h-3V12h3V9.4c0-3 1.8-4.7 4.5-4.7 1.3 0 2.7.2 2.7.2v3h-1.5c-1.5 0-2 .9-2 1.9V12h3.3l-.5 3.5h-2.8v8.4A12 12 0 0 0 24 12z")        "reddit" => lined([<ellipse cx="12" cy="14.5" rx="8.5" ry="6"/>, <path d="M12 8.5 13.5 3l4.5 1.2"/>, <circle cx="19.5" cy="4.5" r="1.5"/>,                           <circle cx="8.8" cy="13.5" r="1" fill="currentColor" stroke="none"/>, <circle cx="15.2" cy="13.5" r="1" fill="currentColor" stroke="none"/>,                           <path d="M9 17c1.8 1.2 4.2 1.2 6 0"/>])        "hackernews" => lined([<rect x="3" y="3" width="18" height="18" rx="3"/>, <path d="m8 7 4 6.5L16 7M12 13.5V18"/>])        "whatsapp" => lined([<path d="M3.5 20.5 5 16a8.5 8.5 0 1 1 3.3 3.2z"/>,                             <path d="M9.2 8.2c-.4 2.9 3.6 6.9 6.6 6.6l.9-1.5-2-1.1-1 .8a5 5 0 0 1-2.7-2.7l.8-1-1.1-2z"/>])        "telegram" => lined([<path d="M21 4 3 11l6 2.5L11 20l3.5-4.2L19 19z"/>, <path d="m9 13.5 7-5.5"/>])        _ => lined([<rect x="2.5" y="5" width="19" height="14" rx="2.5"/>, <path d="m3 7 9 6 9-6"/>])    }}view ShareLinks(url: str, title: str, text: str? = null, via: str? = null, networks: list? = null, mastodon: str = "mastodon.social",                label: str = "Share this post", show_labels: bool = false, size: str = "md", variant: str = "outline",                layout: str = "row", id: str? = null, class: str? = null) {    style global share_links_css    let said = text ?? title    let name = label != "" ? label : "Share this post"    <div id={id} class={ui_cls(["ui-bshare", size != "md" && `ui-bshare-${size}`, variant != "outline" && `ui-bshare-${variant}`,                                show_labels && "ui-bshare-labels", layout == "column" && "ui-bshare-column", class])}>        {label != "" && <p.ui-bshare-label aria-hidden="true">{label}</p>}        <ul.ui-bshare-list aria-label={name}>            {for k in (networks ?? share_links_default) | filter((k) => contains(share_links_all, k)) => {                let net = share_links_name(k)                let mail = net == "Email"                <li key={k}><a href={share_links_href(k, url, title, said, via, mastodon)} data-network={mail ? "email" : k}                       target={mail ? null : "_blank"} rel={mail ? null : "noopener noreferrer"}                       aria-label={mail ? "Share by email" : `Share on ${net} (opens in a new tab)`}                       title={show_labels ? null : (mail ? "Share by email" : `Share on ${net}`)}>                    {share_links_icon(k)}{show_labels && <span>{net}</span>}                </a></li>            }}        </ul>    </div>}
Thread
76Useful? Like it, or add to its thread below.

Thread 0 posts

On ShareLinks: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.