first
This commit is contained in:
@@ -0,0 +1,759 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/404.html">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
<h1>Content not found.</h1>
|
||||
<p>Sorry, can't find this page. Return <a href="/">home</a>.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/404.html` was built on 2026-09-23T23:25:06.281Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,793 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>accessibility | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="accessibility | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/accessibility/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
<h1><span class="brand">Spoonfairies</span> accessibility</h1>
|
||||
<section>
|
||||
<h2 id="program-accessibility-statement">program accessibility statement</h2>
|
||||
<p>tbd</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2 id="site-accessibility-statement">site accessibility statement</h2>
|
||||
<p>This is an accessibility statement from <span class="brand">Spoonfairies</span>.</p>
|
||||
<h3 id="measures-to-support-accessibility">measures to support accessibility</h3>
|
||||
<p><span class="brand">Spoonfairies</span> takes the following measures to ensure accessibility:</p>
|
||||
<ul>
|
||||
<li>Include accessibility throughout our internal policies.</li>
|
||||
<li>Integrate accessibility into our procurement practices.</li>
|
||||
<li>Assign clear accessibility goals and responsibilities.</li>
|
||||
<li>Center disabled people in our program.</li>
|
||||
</ul>
|
||||
<h3 id="conformance-status">conformance status</h3>
|
||||
<p>The <a href="https://www.w3.org/WAI/standards-guidelines/wcag/" target="_blank" rel="external">Web Content Accessibility Guidelines (WCAG)</a> defines requirements for designers and developers to improve accessibility for people with disabilities. It defines three levels of conformance: Level A, Level AA, and Level AAA. <span class="brand">Spoonfairies</span> is, to the best of our knowledge, fully conformant with WCAG 2.2 level AA. Fully conformant means that the content fully conforms to the accessibility standard without any exceptions.</p>
|
||||
<h3 id="feedback">feedback</h3>
|
||||
<p>We welcome your feedback on the accessibility of <span class="brand">Spoonfairies</span>. Please let us know if you encounter accessibility barriers via <a href="https://leecat.art" target="_blank" rel="external">any of the contact methods listed for Lee Cattarin</a>.</p>
|
||||
<h3 id="technical-specifications">technical specifications</h3>
|
||||
<p>Accessibility of <span class="brand">Spoonfairies</span> relies on the following technologies to work with the particular combination of web browser and any assistive technologies or plugins installed on your computer:</p>
|
||||
<ul>
|
||||
<li>HTML</li>
|
||||
<li>CSS</li>
|
||||
<li>JavaScript</li>
|
||||
</ul>
|
||||
<p>These technologies are relied upon for conformance with the accessibility standards used.</p>
|
||||
<h3 id="assessment-approach">assessment approach</h3>
|
||||
<p>We assessed the accessibility of <span class="brand">Spoonfairies</span> by the following approaches:</p>
|
||||
<ul>
|
||||
<li>Self-evaluation</li>
|
||||
</ul>
|
||||
<h3 id="date">date</h3>
|
||||
<p>This statement was created on 4 February 2026 using the <a href="https://www.w3.org/WAI/planning/statements/" target="_blank" rel="external">W3C Accessibility Statement Generator Tool</a>.</p>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/accessibility/` was built on 2026-09-23T23:25:06.281Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,762 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>attribution | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="attribution | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/attribution/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
<h1>attribution</h1>
|
||||
<p>This site is built using <a href="https://www.11ty.dev/" target="_blank" rel="external">Eleventy v3.1.5</a> by <a href="https://leecat.art" target="_blank" rel="external">Lee Cattarin</a>. It is hosted by <a href="https://heckin.technology/" target="_blank" rel="external">heckin' technology</a>, "a small, weird, mostly indigenous, autistic game and software and art cooperative."</p>
|
||||
<p>The font is <a href="https://brailleinstitute.org/freefont" target="_blank" rel="external">Atkinson Hyperlegible Next</a>, specifically designed for low-vision readers to improve character recognition.</p>
|
||||
<p>v1 of this site was built from the <a href="https://github.com/11ty/eleventy-base-blog" target="_blank" rel="external">Eleventy base blog template</a>.</p>
|
||||
<p>This site is created <a href="https://declare-ai.org/1.0.0/none.html" target="_blank" rel="external">without the use of generative AI</a>.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/attribution/` was built on 2026-09-23T23:25:06.281Z -->
|
||||
</body>
|
||||
</html>
|
||||
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
@@ -0,0 +1,812 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>get involved | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="get involved | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/get-involved/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/" aria-current="page">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
<h1>get involved with <span class="brand">Spoonfairies</span></h1>
|
||||
<section>
|
||||
<h2 id="location">location</h2>
|
||||
<p><span class="brand">Spoonfairies</span> is currently focused on Seattle and the Puget Sound, though we can accept digital-only providers from all over. If you're outside of this region and would like to offer physical services, please <a href="/team#core">talk to someone on the core team</a> about extending <span class="brand">Spoonfairies</span>' reach.</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2 id="our-values">our values</h2>
|
||||
<p>We ask that all providers and clients uphold our core values as part of their participation.</p>
|
||||
<ul class="long-items">
|
||||
<li>
|
||||
<strong>Community:</strong> we aim to create, nurture, and sustain a
|
||||
community of providers actively seeking to promote collective wellness,
|
||||
healing, and support for marginalized people.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Adaptability:</strong> we embrace change and challenges with
|
||||
enthusiasm and creativity. Our core purpose is to cater our model to meet
|
||||
the needs of those in our community, ever growing and furthering the goals
|
||||
of social equitability, justice, and wellness. This means our model and all
|
||||
outreach should be accessible across all identities & needs.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Love:</strong> we approach all interactions wholeheartedly, with
|
||||
the primary purpose of extending love and kindness into our community.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Health:</strong> we disrupt the cycle of capitalist exploitation
|
||||
by honoring the health and wellness of our providers and members; we honor
|
||||
our own health needs and actively encourage our community to prioritize
|
||||
their well-being over capitalist interest by facilitating livable wages,
|
||||
communal support, and collective healing.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Dignity:</strong> we create systems and processes that allow for
|
||||
providers to fully experience the diverse complexities of everyday life.
|
||||
We believe that every person deserves to have livable wages and sustainable
|
||||
work/life balance, and we celebrate our providers' "no".
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2 id="as-a-provider">as a provider</h2>
|
||||
<p>Sign up using <a href="https://airtable.com/appTNSejknPJEEdrN/pagzaSgmO9bnFVgTt/form" target="_blank" rel="external">our sign-up form</a> — or <a href="mailto:tech@spoonfairies.help?subject=Spoonfairies signup"> email Lee</a> if Airtable (our form provider) is inaccessible to you.</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2 id="as-a-client">as a client</h2>
|
||||
<p>View the <a href="/providers">provider directory</a> or <a href="/services">browse by service</a> to find relevant providers.</p>
|
||||
<p>If a given provider does not have a contact method that works for you, reach out to <a href="/team">someone on the team</a> and we'll get in contact with them for you.</p>
|
||||
<p>Overwhelmed by choices? <a href="mailto:info@spoonfairies.help?subject=Spoonfairies service and provider discovery">Email Bex</a> if you need help navigating providers and services.</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2 id="testimonials">testimonials</h2>
|
||||
<p>Clients and providers are strongly encouraged to submit testimonials about their experiences with a given provider or with the Spoonfairies program as a whole.</p>
|
||||
<p>You can submit a testimonial using our <a href="https://airtable.com/appTNSejknPJEEdrN/pagBkMSqVTVXDCkAa/form" target="_blank" rel="external">testimonial form</a> — or <a href="mailto:tech@spoonfairies.help?subject=Spoonfairies testimonials">email Lee</a> if Airtable (our form provider) is inaccessible to you.</p>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/get-involved/` was built on 2026-09-23T23:25:06.281Z -->
|
||||
</body>
|
||||
</html>
|
||||
+845
@@ -0,0 +1,845 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
<section>
|
||||
<h1>about <span class="brand">Spoonfairies</span></h1>
|
||||
<p><span class="brand">Spoonfairies</span> is a project to connect people: those who need help and those who can provide it, with the ability to move fluidly between these groups or occupy both.</p>
|
||||
<p><span class="brand">Spoonfairies</span> is currently focused on Seattle and the Puget Sound region, but digital-only participation can come from all over.</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2 id="ethos">ethos</h2>
|
||||
<h3 id="universal-worth">universal worth</h3>
|
||||
<p>Everyone needs and deserves help, and everyone has skills that can better their community. Everyone deserves access to the support and income they need to live their best life.</p>
|
||||
<h3 id="person-centered-pricing">person-centered pricing</h3>
|
||||
<p>We are all of equal value and deserve living wages for expending our time and energy. No service is inherently worth any more than another because no person or their time is worth more than another’s.</p>
|
||||
<h3 id="alternatives-to-capitalism">alternatives to capitalism</h3>
|
||||
<p>We must build our own support system to build and keep wealth within our communities and provide the services and infrastructures we need to survive.</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2 id="spoonfairies-can-look-like..."><span class="brand">Spoonfairies</span> can look like...</h2>
|
||||
<h3 id="client">client</h3>
|
||||
<ul>
|
||||
<li>J has a sister with long COVID who needs help with some household tasks. He wants to gift her 5 hrs of cleaning services, so he sets her up with a <span class="brand">Spoonfairies</span> provider he's already arranged payment with</li>
|
||||
<li>M is a busy parent who can forget to take care of herself. She signs up for a weekly check-in service to ensure her own goals move forward</li>
|
||||
<li>X is a tech worker who hates going to the grocery store. Ze finds a provider to help with weekly grocery delivery and a monthly Costco run</li>
|
||||
</ul>
|
||||
<h3 id="provider">provider</h3>
|
||||
<ul>
|
||||
<li>L is a stay at home parent with ADHD who loves to cook and provides weekly meal prep & delivery for 25 neurodivergent queerdos in her area at good hourly wage</li>
|
||||
<li>Q, a car mechanic, owns a queer/neurodivergent friendly business. She offers 10 hrs of services per month to <span class="brand">Spoonfairies</span> clients at friends-and-family rates</li>
|
||||
<li>D has a part-time job bookkeeping for a larger firm. In their free time, they use their skills to provide monthly accounting services to a handful of clients’ small businesses in exchange for business discounts</li>
|
||||
</ul>
|
||||
<h3 id="or-both">or both</h3>
|
||||
<ul>
|
||||
<li>Z, an agoraphobic autistic with OCD who is excellent at organization and graphic design, provides admin design services and receives grocery/med delivery and occasional cleaning help.</li>
|
||||
<li>C is on disability and navigating income/wealth cap. Xe runs virtual body doubling services and barters some of xe's work for other services.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2 id="example-services">example services</h2>
|
||||
<h3 id="executive-function">executive function</h3>
|
||||
<h4 id="recurring">recurring</h4>
|
||||
<ul>
|
||||
<li>Weekly check-ins – "Life Boss"</li>
|
||||
<li>Life coach – neurodivergent strategies & resources</li>
|
||||
</ul>
|
||||
<h4 id="on-demand">on demand</h4>
|
||||
<ul>
|
||||
<li>Body doubling/errand hangs</li>
|
||||
<li>Custom organizers/graphics for household/personal routines</li>
|
||||
<li>Neurodivergent advocate/educator (landlords, bosses, families/partners)</li>
|
||||
</ul>
|
||||
<h3 id="physical-%26-mental-health">physical & mental health</h3>
|
||||
<h4 id="recurring-1">recurring</h4>
|
||||
<ul>
|
||||
<li>Prescription pick-up/renewal reminders</li>
|
||||
<li>Transportation to/from appointments</li>
|
||||
<li>Medical advocacy at appointments</li>
|
||||
<li>Peer counseling</li>
|
||||
</ul>
|
||||
<h4 id="on-demand-1">on demand</h4>
|
||||
<ul>
|
||||
<li>Provider research</li>
|
||||
<li>Appointment scheduling</li>
|
||||
<li>Benefits navigation</li>
|
||||
</ul>
|
||||
<h3 id="home-and-personal-spaces">home and personal spaces</h3>
|
||||
<h4 id="recurring-2">recurring</h4>
|
||||
<ul>
|
||||
<li>Recurring cleaning/chore help</li>
|
||||
<li>Recurring grocery delivery/meal plan/prep</li>
|
||||
<li>Recurring appliance/vehicle maintenance</li>
|
||||
</ul>
|
||||
<h4 id="on-demand-2">on demand</h4>
|
||||
<ul>
|
||||
<li>Organization/room design</li>
|
||||
<li>De-funk your space</li>
|
||||
</ul>
|
||||
<h3 id="administrative">administrative</h3>
|
||||
<h4 id="recurring-3">recurring</h4>
|
||||
<ul>
|
||||
<li>Email/calendar management</li>
|
||||
<li>Scheduling support</li>
|
||||
<li>Budget tracking/personal finance support</li>
|
||||
</ul>
|
||||
<h4 id="on-demand-3">on demand</h4>
|
||||
<ul>
|
||||
<li>Event planning</li>
|
||||
<li>Job search support</li>
|
||||
<li>Travel booking/vacation planning</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/` was built on 2026-09-23T23:25:06.281Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,770 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>privacy | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="privacy | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/privacy/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
<h1>privacy</h1>
|
||||
<section>
|
||||
<h2 id="provider-privacy">provider privacy</h2>
|
||||
<p>Providers sign up via Airtable. <a href="https://www.airtable.com/company/privacy#a-use-of-content" target="_blank" rel="external">Read more on Airtable's privacy policy with regard to user content</a>.</p>
|
||||
<p>Airtable access is limited to the <a href="/team#core">core <span class="brand">Spoonfairies</span> organizing team</a>.</p>
|
||||
<p>Sign-up makes clear what provided information is available on our public-facing website. No other information is provided to prospective clients or other parties.</p>
|
||||
<p>Providers may <a href="/team#core">reach out to someone on the core team</a> at any time regarding removal from the site and deletion of their information.</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2 id="client-privacy">client privacy</h2>
|
||||
<p>Clients reach out directly to providers using published contact information. No client information is handled by the <span class="brand">Spoonfairies</span> team.</p>
|
||||
<p><strong>For providers:</strong> if you are looking for places to store client data, we recommend <a href="https://cryptpad.fr/" target="_blank" rel="external">CryptPad</a> which is a fully encrypted and privacy-conscious alternative to services like Google Drive. It offers 1GB of free storage, or further storage on subscription plans.</p>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/privacy/` was built on 2026-09-23T23:25:06.281Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,812 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>Bex M. | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="Bex M. | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/providers/bex-m/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<article>
|
||||
<h1>Bex M.
|
||||
<span class="pronouns">vey/she</span>
|
||||
</h1>
|
||||
|
||||
<div class="post-metadata">
|
||||
<p class="post-time">last updated <time datetime="2026-02-09">February 9, 2026</time></p>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/tutoring/" class="post-tag" title="browse providers who offer tutoring">tutoring</a>, </li>
|
||||
<li><a href="/services/project-management/" class="post-tag" title="browse providers who offer project management">project management</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/event-planning/" class="post-tag" title="browse providers who offer event planning">event planning</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="location">Phinney Ridge</p>
|
||||
|
||||
</div>
|
||||
|
||||
<h2 id="bio">bio</h2>
|
||||
<p>fox fairy with background in education, administration, and social work.</p>
|
||||
<h2 id="services">services</h2>
|
||||
<ul>
|
||||
<li>weekly check-in/project management services</li>
|
||||
<li>K-12 tutoring, particular expertise in English and physics</li>
|
||||
<li>administrative support (drafting, copy editing, scheduling, travel & event planning)</li>
|
||||
</ul>
|
||||
<h3 id="payment">payment</h3>
|
||||
<ul>
|
||||
<li>sliding scale $30-50/hr, billable by quarter hour</li>
|
||||
<li>services swap/skill share</li>
|
||||
<li>pro bono option available as needed</li>
|
||||
</ul>
|
||||
<h3 id="time-constraints">time constraints</h3>
|
||||
<p>5 hrs per week max (under current circumstances)</p>
|
||||
|
||||
|
||||
|
||||
<h2>contact information</h2>
|
||||
<ul class="contacts">
|
||||
|
||||
<li>
|
||||
|
||||
<a href="mailto:fairy.lady@gmail.com?subject=Spoonfairies" target="_blank">fairy.lady [at] gmail [dot] com</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<section class="related-providers">
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/providers/bex-m/` was built on 2026-09-23T23:25:06.285Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,808 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>Blythe S. | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="Blythe S. | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/providers/blythe-s/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<article>
|
||||
<h1>Blythe S.
|
||||
<span class="pronouns">she/her</span>
|
||||
</h1>
|
||||
|
||||
<div class="post-metadata">
|
||||
<p class="post-time">last updated <time datetime="2026-03-17">March 17, 2026</time></p>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/hairdressing/" class="post-tag" title="browse providers who offer hairdressing">hairdressing</a>, </li>
|
||||
<li><a href="/services/hygiene/" class="post-tag" title="browse providers who offer hygiene">hygiene</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="location">West Seattle</p>
|
||||
|
||||
</div>
|
||||
|
||||
<h2 id="bio">bio</h2>
|
||||
<p>I am an ally, friend and professional hairdresser. I am happiest when interacting with and learning about new people. I do my best to listen and observe as I get to know people.</p>
|
||||
<h2 id="services">services</h2>
|
||||
<p>I can do haircutting and styling. I may be able to offer more if I learn of people’s different needs. I can also give personal hygiene products for those in need.</p>
|
||||
<h3 id="payment">payment</h3>
|
||||
<p>Pro bono</p>
|
||||
<h3 id="time-constraints">time constraints</h3>
|
||||
<p>Appointment only, not available Monday eve or Sunday.</p>
|
||||
|
||||
|
||||
|
||||
<h2>contact information</h2>
|
||||
<ul class="contacts">
|
||||
|
||||
<li>
|
||||
|
||||
<a href="mailto:glamouramalola@msn.com?subject=Spoonfairies" target="_blank">glamouramalola [at] msn [dot] com</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
<a href="tel:206-910-8239" target="_blank">206-910-8239</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<section class="related-providers">
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/providers/blythe-s/` was built on 2026-09-23T23:25:06.285Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,802 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>Brooke O. | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="Brooke O. | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/providers/brooke-o/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<article>
|
||||
<h1>Brooke O.
|
||||
<span class="pronouns">it/its</span>
|
||||
</h1>
|
||||
|
||||
<div class="post-metadata">
|
||||
<p class="post-time">last updated <time datetime="2026-02-09">February 9, 2026</time></p>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/seamstress/" class="post-tag" title="browse providers who offer seamstress">seamstress</a>, </li>
|
||||
<li><a href="/services/repair/" class="post-tag" title="browse providers who offer repair">repair</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="location">Vashon</p>
|
||||
|
||||
</div>
|
||||
|
||||
<h2 id="bio">bio</h2>
|
||||
<p>I am a self-taught seamstress and fashion-minded punk. I have been stitching my jackets back together for as long as they've been falling apart, generally with a mind towards visible mending and eye-catching colors.</p>
|
||||
<h2 id="services">services</h2>
|
||||
<p>Clothing/textile repair and upcycling, with a strong preference for jackets and/or heavier fabrics</p>
|
||||
<h3 id="payment">payment</h3>
|
||||
<p>cash/barter</p>
|
||||
|
||||
|
||||
|
||||
<h2>contact information</h2>
|
||||
<ul class="contacts">
|
||||
|
||||
<li>
|
||||
|
||||
<a href="mailto:bsosment@gmail.com?subject=Spoonfairies" target="_blank">bsosment [at] gmail [dot] com</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<section class="related-providers">
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/providers/brooke-o/` was built on 2026-09-23T23:25:06.285Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,905 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/providers/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/" aria-current="page">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>all providers</h1>
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/blythe-s/" class="providerlist-link" aria-label="Blythe S.
|
||||
she/her
|
||||
is based out of West Seattle
|
||||
">
|
||||
Blythe S.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">West Seattle</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/hairdressing/" class="post-tag" title="browse providers who offer hairdressing">hairdressing</a>, </li>
|
||||
<li><a href="/services/hygiene/" class="post-tag" title="browse providers who offer hygiene">hygiene</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/rain-v/" class="providerlist-link" aria-label="Rain V.
|
||||
she/they
|
||||
is based out of Redmond
|
||||
">
|
||||
Rain V.
|
||||
|
||||
<span class="pronouns">she/they</span>
|
||||
|
||||
<span class="location">Redmond</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/funds/" class="post-tag" title="browse providers who offer funds">funds</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a>, </li>
|
||||
<li><a href="/services/errands/" class="post-tag" title="browse providers who offer errands">errands</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/roz-m/" class="providerlist-link" aria-label="Roz M.
|
||||
she/her
|
||||
is based out of Wallingford
|
||||
">
|
||||
Roz M.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">Wallingford</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/body-doubling/" class="post-tag" title="browse providers who offer body doubling">body doubling</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/medical-advocate/" class="post-tag" title="browse providers who offer medical advocate">medical advocate</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/lee-c/" class="providerlist-link" aria-label="Lee C.
|
||||
ze/he
|
||||
is digital-only">
|
||||
Lee C.
|
||||
|
||||
<span class="pronouns">ze/he</span>
|
||||
|
||||
<span class="online">digital-only</span>
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/brooke-o/" class="providerlist-link" aria-label="Brooke O.
|
||||
it/its
|
||||
is based out of Vashon
|
||||
">
|
||||
Brooke O.
|
||||
|
||||
<span class="pronouns">it/its</span>
|
||||
|
||||
<span class="location">Vashon</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/seamstress/" class="post-tag" title="browse providers who offer seamstress">seamstress</a>, </li>
|
||||
<li><a href="/services/repair/" class="post-tag" title="browse providers who offer repair">repair</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/bex-m/" class="providerlist-link" aria-label="Bex M.
|
||||
vey/she
|
||||
is based out of Phinney Ridge
|
||||
">
|
||||
Bex M.
|
||||
|
||||
<span class="pronouns">vey/she</span>
|
||||
|
||||
<span class="location">Phinney Ridge</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/tutoring/" class="post-tag" title="browse providers who offer tutoring">tutoring</a>, </li>
|
||||
<li><a href="/services/project-management/" class="post-tag" title="browse providers who offer project management">project management</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/event-planning/" class="post-tag" title="browse providers who offer event planning">event planning</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/providers/` was built on 2026-09-23T23:25:06.280Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,819 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>Lee C. | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="Lee C. | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/providers/lee-c/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<article>
|
||||
<h1>Lee C.
|
||||
<span class="pronouns">ze/he</span>
|
||||
</h1>
|
||||
|
||||
<div class="post-metadata">
|
||||
<p class="post-time">last updated <time datetime="2026-02-09">February 9, 2026</time></p>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="online">digital-only</p>
|
||||
</div>
|
||||
|
||||
<h2 id="bio">bio</h2>
|
||||
<p>multidisciplinary artist and crafter. reluctant computer toucher. big fan of birds.</p>
|
||||
<h2 id="services">services</h2>
|
||||
<p>web dev with a focus on accessibility, incl. accessibility reviews for your existing website. spreadsheets galore. basic logos, functional leatherwork, block printed clothing with <a href="https://leecat.art/tag/print" target="_blank">existing designs</a> or your own design (takes more work and resources, add'l cost).</p>
|
||||
<h3 id="payment">payment</h3>
|
||||
<p>sliding scale or skill swap</p>
|
||||
<h3 id="time-constraints">time constraints</h3>
|
||||
<p>*shrug emoji*</p>
|
||||
|
||||
|
||||
|
||||
<h2>contact information</h2>
|
||||
<ul class="contacts">
|
||||
|
||||
<li>
|
||||
|
||||
<a href="https://signal.me/#eu/PcJ86iNQeAbC-SFbxqI00gFQb6zL4Y6QDCsFDU2mokM4K40djsdNYa3hJAee79ll" target="_blank">inherentlee.13 on signal</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
<a href="mailto:lee.cattarin@gmail.com?subject=Spoonfairies" target="_blank">lee.cattarin [at] gmail [dot] com</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
<a href="https://leecat.art/contact" target="_blank">leecat.art/contact</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<section class="related-providers">
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/providers/lee-c/` was built on 2026-09-23T23:25:06.285Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,804 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>Rain V. | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="Rain V. | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/providers/rain-v/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<article>
|
||||
<h1>Rain V.
|
||||
<span class="pronouns">she/they</span>
|
||||
</h1>
|
||||
|
||||
<div class="post-metadata">
|
||||
<p class="post-time">last updated <time datetime="2026-02-13">February 13, 2026</time></p>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/funds/" class="post-tag" title="browse providers who offer funds">funds</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a>, </li>
|
||||
<li><a href="/services/errands/" class="post-tag" title="browse providers who offer errands">errands</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="location">Redmond</p>
|
||||
|
||||
</div>
|
||||
|
||||
<h2 id="bio">bio</h2>
|
||||
<p>I have many crafting hobbies such as woodworking and CNC, but I'm too busy and tired to have much time for them. such is life. I'm a software engineer by trade, living the corpo life I guess ᕕ( ᐛ )ᕗ</p>
|
||||
<h2 id="services">services</h2>
|
||||
<p>mainly money tbh, and I can help with things if you're near Redmond or Bellevue and it doesn't take a lot of time.</p>
|
||||
<h3 id="payment">payment</h3>
|
||||
<p>praise? barter? (clean my place or body doubling with my partner Milo to help him sort and organize his stuff, little art commissions might be cool) or help someone else in this community for me ❤️</p>
|
||||
<h3 id="time-constraints">time constraints</h3>
|
||||
<p>1-2 hours per week, generally low availability so flexibility and planning ahead helps</p>
|
||||
|
||||
|
||||
|
||||
<h2>contact information</h2>
|
||||
<ul class="contacts">
|
||||
|
||||
<li>
|
||||
|
||||
rainybean on discord
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<section class="related-providers">
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/providers/rain-v/` was built on 2026-09-23T23:25:06.286Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,823 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>Roz M. | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="Roz M. | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/providers/roz-m/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<article>
|
||||
<h1>Roz M.
|
||||
<span class="pronouns">she/her</span>
|
||||
</h1>
|
||||
|
||||
<div class="post-metadata">
|
||||
<p class="post-time">last updated <time datetime="2026-02-09">February 9, 2026</time></p>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/body-doubling/" class="post-tag" title="browse providers who offer body doubling">body doubling</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/medical-advocate/" class="post-tag" title="browse providers who offer medical advocate">medical advocate</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="location">Wallingford</p>
|
||||
|
||||
</div>
|
||||
|
||||
<h2 id="bio">bio</h2>
|
||||
<p>Roz Martinez is a Seattle-based artist working predominantly in graphite and ink. She grew up on the east coast, learning how to express herself visually from her mother and grandmother, both of whom studied art extensively.</p>
|
||||
<h2 id="services">services</h2>
|
||||
<ul>
|
||||
<li>Body Doubling</li>
|
||||
<li>Art/Design: commissions, teaching, hosting art parties</li>
|
||||
<li>Mending/sewing</li>
|
||||
<li>Admin: email drafting, phone calls & scripting, desktop research</li>
|
||||
<li>Medical advocate/companion</li>
|
||||
</ul>
|
||||
<h3 id="payment">payment</h3>
|
||||
<p>Open to any upon negotiation</p>
|
||||
<h3 id="time-constraints">time constraints</h3>
|
||||
<p>Can dedicate 1 day per week to <span class="brand">Spoonfairies</span> and other projects</p>
|
||||
|
||||
|
||||
|
||||
<h2>contact information</h2>
|
||||
<ul class="contacts">
|
||||
|
||||
<li>
|
||||
|
||||
<a href="mailto:rozmartinez@gmail.com?subject=Spoonfairies" target="_blank">rozmartinez [at] gmail [dot] com</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
<a href="https://www.rozmartinezfineart.com/" target="_blank">rozmartinezfineart.com</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
<a href="https://bsky.app/profile/rozmartinez.bsky.social" target="_blank">rozmartinez.bsky.social on bsky</a>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
|
||||
<section class="related-providers">
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/providers/roz-m/` was built on 2026-09-23T23:25:06.286Z -->
|
||||
</body>
|
||||
</html>
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
# Sourced from https://codeberg.org/superseriousbusiness/gotosocial/
|
||||
|
||||
# AI scrapers and the like.
|
||||
# https://github.com/ai-robots-txt/ai.robots.txt/
|
||||
User-agent: AddSearchBot
|
||||
User-agent: AI2Bot
|
||||
User-agent: AI2Bot-DeepResearchEval
|
||||
User-agent: Ai2Bot-Dolma
|
||||
User-agent: aiHitBot
|
||||
User-agent: amazon-kendra
|
||||
User-agent: Amazonbot
|
||||
User-agent: AmazonBuyForMe
|
||||
User-agent: Andibot
|
||||
User-agent: Anomura
|
||||
User-agent: anthropic-ai
|
||||
User-agent: Applebot
|
||||
User-agent: Applebot-Extended
|
||||
User-agent: atlassian-bot
|
||||
User-agent: Awario
|
||||
User-agent: bedrockbot
|
||||
User-agent: bigsur.ai
|
||||
User-agent: Bravebot
|
||||
User-agent: Brightbot 1.0
|
||||
User-agent: BuddyBot
|
||||
User-agent: Bytespider
|
||||
User-agent: CCBot
|
||||
User-agent: Channel3Bot
|
||||
User-agent: ChatGLM-Spider
|
||||
User-agent: ChatGPT Agent
|
||||
User-agent: ChatGPT-User
|
||||
User-agent: Claude-SearchBot
|
||||
User-agent: Claude-User
|
||||
User-agent: Claude-Web
|
||||
User-agent: ClaudeBot
|
||||
User-agent: Cloudflare-AutoRAG
|
||||
User-agent: CloudVertexBot
|
||||
User-agent: cohere-ai
|
||||
User-agent: cohere-training-data-crawler
|
||||
User-agent: Cotoyogi
|
||||
User-agent: Crawl4AI
|
||||
User-agent: Crawlspace
|
||||
User-agent: Datenbank Crawler
|
||||
User-agent: DeepSeekBot
|
||||
User-agent: Devin
|
||||
User-agent: Diffbot
|
||||
User-agent: DuckAssistBot
|
||||
User-agent: Echobot Bot
|
||||
User-agent: EchoboxBot
|
||||
User-agent: FacebookBot
|
||||
User-agent: facebookexternalhit
|
||||
User-agent: Factset_spyderbot
|
||||
User-agent: FirecrawlAgent
|
||||
User-agent: FriendlyCrawler
|
||||
User-agent: Gemini-Deep-Research
|
||||
User-agent: Google-CloudVertexBot
|
||||
User-agent: Google-Extended
|
||||
User-agent: Google-Firebase
|
||||
User-agent: Google-NotebookLM
|
||||
User-agent: GoogleAgent-Mariner
|
||||
User-agent: GoogleOther
|
||||
User-agent: GoogleOther-Image
|
||||
User-agent: GoogleOther-Video
|
||||
User-agent: GPTBot
|
||||
User-agent: iAskBot
|
||||
User-agent: iaskspider
|
||||
User-agent: iaskspider/2.0
|
||||
User-agent: IbouBot
|
||||
User-agent: ICC-Crawler
|
||||
User-agent: ImagesiftBot
|
||||
User-agent: imageSpider
|
||||
User-agent: img2dataset
|
||||
User-agent: ISSCyberRiskCrawler
|
||||
User-agent: Kangaroo Bot
|
||||
User-agent: KlaviyoAIBot
|
||||
User-agent: KunatoCrawler
|
||||
User-agent: laion-huggingface-processor
|
||||
User-agent: LAIONDownloader
|
||||
User-agent: LCC
|
||||
User-agent: LinerBot
|
||||
User-agent: Linguee Bot
|
||||
User-agent: LinkupBot
|
||||
User-agent: Manus-User
|
||||
User-agent: meta-externalagent
|
||||
User-agent: Meta-ExternalAgent
|
||||
User-agent: meta-externalfetcher
|
||||
User-agent: Meta-ExternalFetcher
|
||||
User-agent: meta-webindexer
|
||||
User-agent: MistralAI-User
|
||||
User-agent: MistralAI-User/1.0
|
||||
User-agent: MyCentralAIScraperBot
|
||||
User-agent: netEstate Imprint Crawler
|
||||
User-agent: NotebookLM
|
||||
User-agent: NovaAct
|
||||
User-agent: OAI-SearchBot
|
||||
User-agent: omgili
|
||||
User-agent: omgilibot
|
||||
User-agent: OpenAI
|
||||
User-agent: Operator
|
||||
User-agent: PanguBot
|
||||
User-agent: Panscient
|
||||
User-agent: panscient.com
|
||||
User-agent: Perplexity-User
|
||||
User-agent: PerplexityBot
|
||||
User-agent: PetalBot
|
||||
User-agent: PhindBot
|
||||
User-agent: Poggio-Citations
|
||||
User-agent: Poseidon Research Crawler
|
||||
User-agent: QualifiedBot
|
||||
User-agent: QuillBot
|
||||
User-agent: quillbot.com
|
||||
User-agent: SBIntuitionsBot
|
||||
User-agent: Scrapy
|
||||
User-agent: SemrushBot-OCOB
|
||||
User-agent: SemrushBot-SWA
|
||||
User-agent: ShapBot
|
||||
User-agent: Sidetrade indexer bot
|
||||
User-agent: Spider
|
||||
User-agent: TerraCotta
|
||||
User-agent: Thinkbot
|
||||
User-agent: TikTokSpider
|
||||
User-agent: Timpibot
|
||||
User-agent: TwinAgent
|
||||
User-agent: VelenPublicWebCrawler
|
||||
User-agent: WARDBot
|
||||
User-agent: Webzio-Extended
|
||||
User-agent: webzio-extended
|
||||
User-agent: wpbot
|
||||
User-agent: WRTNBot
|
||||
User-agent: YaK
|
||||
User-agent: YandexAdditional
|
||||
User-agent: YandexAdditionalBot
|
||||
User-agent: YouBot
|
||||
User-agent: ZanistaBot
|
||||
Disallow: /
|
||||
|
||||
# Marketing/SEO "intelligence" data scrapers
|
||||
User-agent: AwarioRssBot
|
||||
User-agent: AwarioSmartBot
|
||||
User-agent: DataForSeoBot
|
||||
User-agent: magpie-crawler
|
||||
User-agent: Meltwater
|
||||
User-agent: peer39_crawler
|
||||
User-agent: peer39_crawler/1.0
|
||||
User-agent: PiplBot
|
||||
User-agent: scoop.it
|
||||
User-agent: Seekr
|
||||
Disallow: /
|
||||
|
||||
# Rules for everything else
|
||||
User-agent: *
|
||||
Crawl-delay: 500
|
||||
@@ -0,0 +1,814 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged admin | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged admin | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/admin/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged admin</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/roz-m/" class="providerlist-link" aria-label="Roz M.
|
||||
she/her
|
||||
is based out of Wallingford
|
||||
">
|
||||
Roz M.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">Wallingford</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/body-doubling/" class="post-tag" title="browse providers who offer body doubling">body doubling</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/medical-advocate/" class="post-tag" title="browse providers who offer medical advocate">medical advocate</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/bex-m/" class="providerlist-link" aria-label="Bex M.
|
||||
vey/she
|
||||
is based out of Phinney Ridge
|
||||
">
|
||||
Bex M.
|
||||
|
||||
<span class="pronouns">vey/she</span>
|
||||
|
||||
<span class="location">Phinney Ridge</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/tutoring/" class="post-tag" title="browse providers who offer tutoring">tutoring</a>, </li>
|
||||
<li><a href="/services/project-management/" class="post-tag" title="browse providers who offer project management">project management</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/event-planning/" class="post-tag" title="browse providers who offer event planning">event planning</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/admin/` was built on 2026-09-23T23:25:06.323Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,816 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged art | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged art | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/art/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged art</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/roz-m/" class="providerlist-link" aria-label="Roz M.
|
||||
she/her
|
||||
is based out of Wallingford
|
||||
">
|
||||
Roz M.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">Wallingford</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/body-doubling/" class="post-tag" title="browse providers who offer body doubling">body doubling</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/medical-advocate/" class="post-tag" title="browse providers who offer medical advocate">medical advocate</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/lee-c/" class="providerlist-link" aria-label="Lee C.
|
||||
ze/he
|
||||
is digital-only">
|
||||
Lee C.
|
||||
|
||||
<span class="pronouns">ze/he</span>
|
||||
|
||||
<span class="online">digital-only</span>
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/art/` was built on 2026-09-23T23:25:06.335Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,791 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged body doubling | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged body doubling | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/body-doubling/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged body doubling</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/roz-m/" class="providerlist-link" aria-label="Roz M.
|
||||
she/her
|
||||
is based out of Wallingford
|
||||
">
|
||||
Roz M.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">Wallingford</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/body-doubling/" class="post-tag" title="browse providers who offer body doubling">body doubling</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/medical-advocate/" class="post-tag" title="browse providers who offer medical advocate">medical advocate</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/body-doubling/` was built on 2026-09-23T23:25:06.341Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,815 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged clothing | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged clothing | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/clothing/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged clothing</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/lee-c/" class="providerlist-link" aria-label="Lee C.
|
||||
ze/he
|
||||
is digital-only">
|
||||
Lee C.
|
||||
|
||||
<span class="pronouns">ze/he</span>
|
||||
|
||||
<span class="online">digital-only</span>
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/brooke-o/" class="providerlist-link" aria-label="Brooke O.
|
||||
it/its
|
||||
is based out of Vashon
|
||||
">
|
||||
Brooke O.
|
||||
|
||||
<span class="pronouns">it/its</span>
|
||||
|
||||
<span class="location">Vashon</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/seamstress/" class="post-tag" title="browse providers who offer seamstress">seamstress</a>, </li>
|
||||
<li><a href="/services/repair/" class="post-tag" title="browse providers who offer repair">repair</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/clothing/` was built on 2026-09-23T23:25:06.328Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,815 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged computers | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged computers | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/computers/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged computers</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/rain-v/" class="providerlist-link" aria-label="Rain V.
|
||||
she/they
|
||||
is based out of Redmond
|
||||
">
|
||||
Rain V.
|
||||
|
||||
<span class="pronouns">she/they</span>
|
||||
|
||||
<span class="location">Redmond</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/funds/" class="post-tag" title="browse providers who offer funds">funds</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a>, </li>
|
||||
<li><a href="/services/errands/" class="post-tag" title="browse providers who offer errands">errands</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/lee-c/" class="providerlist-link" aria-label="Lee C.
|
||||
ze/he
|
||||
is digital-only">
|
||||
Lee C.
|
||||
|
||||
<span class="pronouns">ze/he</span>
|
||||
|
||||
<span class="online">digital-only</span>
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/computers/` was built on 2026-09-23T23:25:06.336Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,790 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged errands | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged errands | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/errands/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged errands</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/rain-v/" class="providerlist-link" aria-label="Rain V.
|
||||
she/they
|
||||
is based out of Redmond
|
||||
">
|
||||
Rain V.
|
||||
|
||||
<span class="pronouns">she/they</span>
|
||||
|
||||
<span class="location">Redmond</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/funds/" class="post-tag" title="browse providers who offer funds">funds</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a>, </li>
|
||||
<li><a href="/services/errands/" class="post-tag" title="browse providers who offer errands">errands</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/errands/` was built on 2026-09-23T23:25:06.344Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,790 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged event planning | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged event planning | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/event-planning/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged event planning</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/bex-m/" class="providerlist-link" aria-label="Bex M.
|
||||
vey/she
|
||||
is based out of Phinney Ridge
|
||||
">
|
||||
Bex M.
|
||||
|
||||
<span class="pronouns">vey/she</span>
|
||||
|
||||
<span class="location">Phinney Ridge</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/tutoring/" class="post-tag" title="browse providers who offer tutoring">tutoring</a>, </li>
|
||||
<li><a href="/services/project-management/" class="post-tag" title="browse providers who offer project management">project management</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/event-planning/" class="post-tag" title="browse providers who offer event planning">event planning</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/event-planning/` was built on 2026-09-23T23:25:06.324Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,790 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged funds | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged funds | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/funds/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged funds</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/rain-v/" class="providerlist-link" aria-label="Rain V.
|
||||
she/they
|
||||
is based out of Redmond
|
||||
">
|
||||
Rain V.
|
||||
|
||||
<span class="pronouns">she/they</span>
|
||||
|
||||
<span class="location">Redmond</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/funds/" class="post-tag" title="browse providers who offer funds">funds</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a>, </li>
|
||||
<li><a href="/services/errands/" class="post-tag" title="browse providers who offer errands">errands</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/funds/` was built on 2026-09-23T23:25:06.343Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,788 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged hairdressing | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged hairdressing | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/hairdressing/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged hairdressing</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/blythe-s/" class="providerlist-link" aria-label="Blythe S.
|
||||
she/her
|
||||
is based out of West Seattle
|
||||
">
|
||||
Blythe S.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">West Seattle</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/hairdressing/" class="post-tag" title="browse providers who offer hairdressing">hairdressing</a>, </li>
|
||||
<li><a href="/services/hygiene/" class="post-tag" title="browse providers who offer hygiene">hygiene</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/hairdressing/` was built on 2026-09-23T23:25:06.345Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,788 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged hygiene | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged hygiene | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/hygiene/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged hygiene</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/blythe-s/" class="providerlist-link" aria-label="Blythe S.
|
||||
she/her
|
||||
is based out of West Seattle
|
||||
">
|
||||
Blythe S.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">West Seattle</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/hairdressing/" class="post-tag" title="browse providers who offer hairdressing">hairdressing</a>, </li>
|
||||
<li><a href="/services/hygiene/" class="post-tag" title="browse providers who offer hygiene">hygiene</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/hygiene/` was built on 2026-09-23T23:25:06.346Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,830 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>services | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="services | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/" aria-current="page">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
<h1>all services</h1>
|
||||
|
||||
<p>Services are listed alphabetically.</p>
|
||||
|
||||
<ul class="service-list">
|
||||
|
||||
|
||||
<li class="service"><a href="/services/admin/" title="browse providers who offer admin">admin</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/art/" title="browse providers who offer art">art</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/body-doubling/" title="browse providers who offer body doubling">body doubling</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/clothing/" title="browse providers who offer clothing">clothing</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/computers/" title="browse providers who offer computers">computers</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/errands/" title="browse providers who offer errands">errands</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/event-planning/" title="browse providers who offer event planning">event planning</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/funds/" title="browse providers who offer funds">funds</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/hairdressing/" title="browse providers who offer hairdressing">hairdressing</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/hygiene/" title="browse providers who offer hygiene">hygiene</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/logos/" title="browse providers who offer logos">logos</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/medical-advocate/" title="browse providers who offer medical advocate">medical advocate</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/mending/" title="browse providers who offer mending">mending</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/project-management/" title="browse providers who offer project management">project management</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/providers/" title="browse providers who offer providers">providers</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/repair/" title="browse providers who offer repair">repair</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/seamstress/" title="browse providers who offer seamstress">seamstress</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/software/" title="browse providers who offer software">software</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/spreadsheets/" title="browse providers who offer spreadsheets">spreadsheets</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/tutoring/" title="browse providers who offer tutoring">tutoring</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/web-accessibility/" title="browse providers who offer web accessibility">web accessibility</a></li>
|
||||
|
||||
|
||||
<li class="service"><a href="/services/webdev/" title="browse providers who offer webdev">webdev</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/` was built on 2026-09-23T23:25:06.280Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,792 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged logos | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged logos | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/logos/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged logos</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/lee-c/" class="providerlist-link" aria-label="Lee C.
|
||||
ze/he
|
||||
is digital-only">
|
||||
Lee C.
|
||||
|
||||
<span class="pronouns">ze/he</span>
|
||||
|
||||
<span class="online">digital-only</span>
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/logos/` was built on 2026-09-23T23:25:06.334Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,791 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged medical advocate | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged medical advocate | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/medical-advocate/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged medical advocate</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/roz-m/" class="providerlist-link" aria-label="Roz M.
|
||||
she/her
|
||||
is based out of Wallingford
|
||||
">
|
||||
Roz M.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">Wallingford</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/body-doubling/" class="post-tag" title="browse providers who offer body doubling">body doubling</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/medical-advocate/" class="post-tag" title="browse providers who offer medical advocate">medical advocate</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/medical-advocate/` was built on 2026-09-23T23:25:06.342Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,814 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged mending | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged mending | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/mending/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged mending</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/roz-m/" class="providerlist-link" aria-label="Roz M.
|
||||
she/her
|
||||
is based out of Wallingford
|
||||
">
|
||||
Roz M.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">Wallingford</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/body-doubling/" class="post-tag" title="browse providers who offer body doubling">body doubling</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/medical-advocate/" class="post-tag" title="browse providers who offer medical advocate">medical advocate</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/brooke-o/" class="providerlist-link" aria-label="Brooke O.
|
||||
it/its
|
||||
is based out of Vashon
|
||||
">
|
||||
Brooke O.
|
||||
|
||||
<span class="pronouns">it/its</span>
|
||||
|
||||
<span class="location">Vashon</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/seamstress/" class="post-tag" title="browse providers who offer seamstress">seamstress</a>, </li>
|
||||
<li><a href="/services/repair/" class="post-tag" title="browse providers who offer repair">repair</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/mending/` was built on 2026-09-23T23:25:06.329Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,790 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged project management | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged project management | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/project-management/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged project management</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/bex-m/" class="providerlist-link" aria-label="Bex M.
|
||||
vey/she
|
||||
is based out of Phinney Ridge
|
||||
">
|
||||
Bex M.
|
||||
|
||||
<span class="pronouns">vey/she</span>
|
||||
|
||||
<span class="location">Phinney Ridge</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/tutoring/" class="post-tag" title="browse providers who offer tutoring">tutoring</a>, </li>
|
||||
<li><a href="/services/project-management/" class="post-tag" title="browse providers who offer project management">project management</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/event-planning/" class="post-tag" title="browse providers who offer event planning">event planning</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/project-management/` was built on 2026-09-23T23:25:06.322Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,906 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged providers | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged providers | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/providers/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged providers</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/blythe-s/" class="providerlist-link" aria-label="Blythe S.
|
||||
she/her
|
||||
is based out of West Seattle
|
||||
">
|
||||
Blythe S.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">West Seattle</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/hairdressing/" class="post-tag" title="browse providers who offer hairdressing">hairdressing</a>, </li>
|
||||
<li><a href="/services/hygiene/" class="post-tag" title="browse providers who offer hygiene">hygiene</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/rain-v/" class="providerlist-link" aria-label="Rain V.
|
||||
she/they
|
||||
is based out of Redmond
|
||||
">
|
||||
Rain V.
|
||||
|
||||
<span class="pronouns">she/they</span>
|
||||
|
||||
<span class="location">Redmond</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/funds/" class="post-tag" title="browse providers who offer funds">funds</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a>, </li>
|
||||
<li><a href="/services/errands/" class="post-tag" title="browse providers who offer errands">errands</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/roz-m/" class="providerlist-link" aria-label="Roz M.
|
||||
she/her
|
||||
is based out of Wallingford
|
||||
">
|
||||
Roz M.
|
||||
|
||||
<span class="pronouns">she/her</span>
|
||||
|
||||
<span class="location">Wallingford</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/body-doubling/" class="post-tag" title="browse providers who offer body doubling">body doubling</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/medical-advocate/" class="post-tag" title="browse providers who offer medical advocate">medical advocate</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/lee-c/" class="providerlist-link" aria-label="Lee C.
|
||||
ze/he
|
||||
is digital-only">
|
||||
Lee C.
|
||||
|
||||
<span class="pronouns">ze/he</span>
|
||||
|
||||
<span class="online">digital-only</span>
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/brooke-o/" class="providerlist-link" aria-label="Brooke O.
|
||||
it/its
|
||||
is based out of Vashon
|
||||
">
|
||||
Brooke O.
|
||||
|
||||
<span class="pronouns">it/its</span>
|
||||
|
||||
<span class="location">Vashon</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/seamstress/" class="post-tag" title="browse providers who offer seamstress">seamstress</a>, </li>
|
||||
<li><a href="/services/repair/" class="post-tag" title="browse providers who offer repair">repair</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/bex-m/" class="providerlist-link" aria-label="Bex M.
|
||||
vey/she
|
||||
is based out of Phinney Ridge
|
||||
">
|
||||
Bex M.
|
||||
|
||||
<span class="pronouns">vey/she</span>
|
||||
|
||||
<span class="location">Phinney Ridge</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/tutoring/" class="post-tag" title="browse providers who offer tutoring">tutoring</a>, </li>
|
||||
<li><a href="/services/project-management/" class="post-tag" title="browse providers who offer project management">project management</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/event-planning/" class="post-tag" title="browse providers who offer event planning">event planning</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/providers/` was built on 2026-09-23T23:25:06.280Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,790 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged repair | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged repair | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/repair/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged repair</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/brooke-o/" class="providerlist-link" aria-label="Brooke O.
|
||||
it/its
|
||||
is based out of Vashon
|
||||
">
|
||||
Brooke O.
|
||||
|
||||
<span class="pronouns">it/its</span>
|
||||
|
||||
<span class="location">Vashon</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/seamstress/" class="post-tag" title="browse providers who offer seamstress">seamstress</a>, </li>
|
||||
<li><a href="/services/repair/" class="post-tag" title="browse providers who offer repair">repair</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/repair/` was built on 2026-09-23T23:25:06.326Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,790 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged seamstress | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged seamstress | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/seamstress/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged seamstress</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/brooke-o/" class="providerlist-link" aria-label="Brooke O.
|
||||
it/its
|
||||
is based out of Vashon
|
||||
">
|
||||
Brooke O.
|
||||
|
||||
<span class="pronouns">it/its</span>
|
||||
|
||||
<span class="location">Vashon</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/seamstress/" class="post-tag" title="browse providers who offer seamstress">seamstress</a>, </li>
|
||||
<li><a href="/services/repair/" class="post-tag" title="browse providers who offer repair">repair</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/mending/" class="post-tag" title="browse providers who offer mending">mending</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/seamstress/` was built on 2026-09-23T23:25:06.325Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,815 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged software | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged software | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/software/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged software</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/rain-v/" class="providerlist-link" aria-label="Rain V.
|
||||
she/they
|
||||
is based out of Redmond
|
||||
">
|
||||
Rain V.
|
||||
|
||||
<span class="pronouns">she/they</span>
|
||||
|
||||
<span class="location">Redmond</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/funds/" class="post-tag" title="browse providers who offer funds">funds</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a>, </li>
|
||||
<li><a href="/services/errands/" class="post-tag" title="browse providers who offer errands">errands</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/lee-c/" class="providerlist-link" aria-label="Lee C.
|
||||
ze/he
|
||||
is digital-only">
|
||||
Lee C.
|
||||
|
||||
<span class="pronouns">ze/he</span>
|
||||
|
||||
<span class="online">digital-only</span>
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/software/` was built on 2026-09-23T23:25:06.338Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,792 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged spreadsheets | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged spreadsheets | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/spreadsheets/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged spreadsheets</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/lee-c/" class="providerlist-link" aria-label="Lee C.
|
||||
ze/he
|
||||
is digital-only">
|
||||
Lee C.
|
||||
|
||||
<span class="pronouns">ze/he</span>
|
||||
|
||||
<span class="online">digital-only</span>
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/spreadsheets/` was built on 2026-09-23T23:25:06.332Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,790 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged tutoring | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged tutoring | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/tutoring/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged tutoring</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/bex-m/" class="providerlist-link" aria-label="Bex M.
|
||||
vey/she
|
||||
is based out of Phinney Ridge
|
||||
">
|
||||
Bex M.
|
||||
|
||||
<span class="pronouns">vey/she</span>
|
||||
|
||||
<span class="location">Phinney Ridge</span>
|
||||
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/tutoring/" class="post-tag" title="browse providers who offer tutoring">tutoring</a>, </li>
|
||||
<li><a href="/services/project-management/" class="post-tag" title="browse providers who offer project management">project management</a>, </li>
|
||||
<li><a href="/services/admin/" class="post-tag" title="browse providers who offer admin">admin</a>, </li>
|
||||
<li><a href="/services/event-planning/" class="post-tag" title="browse providers who offer event planning">event planning</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/tutoring/` was built on 2026-09-23T23:25:06.314Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,792 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged web accessibility | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged web accessibility | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/web-accessibility/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged web accessibility</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/lee-c/" class="providerlist-link" aria-label="Lee C.
|
||||
ze/he
|
||||
is digital-only">
|
||||
Lee C.
|
||||
|
||||
<span class="pronouns">ze/he</span>
|
||||
|
||||
<span class="online">digital-only</span>
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/web-accessibility/` was built on 2026-09-23T23:25:06.331Z -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,792 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>providers tagged webdev | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="providers tagged webdev | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/services/webdev/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
|
||||
<h1>providers tagged webdev</h1>
|
||||
|
||||
|
||||
<ul class="providerlist">
|
||||
|
||||
<li class="providerlist-item">
|
||||
<a href="/providers/lee-c/" class="providerlist-link" aria-label="Lee C.
|
||||
ze/he
|
||||
is digital-only">
|
||||
Lee C.
|
||||
|
||||
<span class="pronouns">ze/he</span>
|
||||
|
||||
<span class="online">digital-only</span>
|
||||
</a>
|
||||
<p class="visually-hidden">services this provider offers are </p>
|
||||
<ul class="taglist">
|
||||
<li><a href="/services/providers/" class="post-tag" title="browse providers who offer providers">providers</a>, </li>
|
||||
<li><a href="/services/webdev/" class="post-tag" title="browse providers who offer webdev">webdev</a>, </li>
|
||||
<li><a href="/services/web-accessibility/" class="post-tag" title="browse providers who offer web accessibility">web accessibility</a>, </li>
|
||||
<li><a href="/services/spreadsheets/" class="post-tag" title="browse providers who offer spreadsheets">spreadsheets</a>, </li>
|
||||
<li><a href="/services/logos/" class="post-tag" title="browse providers who offer logos">logos</a>, </li>
|
||||
<li><a href="/services/clothing/" class="post-tag" title="browse providers who offer clothing">clothing</a>, </li>
|
||||
<li><a href="/services/art/" class="post-tag" title="browse providers who offer art">art</a>, </li>
|
||||
<li><a href="/services/computers/" class="post-tag" title="browse providers who offer computers">computers</a>, </li>
|
||||
<li><a href="/services/software/" class="post-tag" title="browse providers who offer software">software</a></li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/services/webdev/` was built on 2026-09-23T23:25:06.330Z -->
|
||||
</body>
|
||||
</html>
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/providers/bex-m/</loc>
|
||||
<lastmod>2026-02-09</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/providers/brooke-o/</loc>
|
||||
<lastmod>2026-02-09</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/providers/lee-c/</loc>
|
||||
<lastmod>2026-02-09</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/providers/roz-m/</loc>
|
||||
<lastmod>2026-02-09</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/providers/rain-v/</loc>
|
||||
<lastmod>2026-02-13</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/providers/blythe-s/</loc>
|
||||
<lastmod>2026-03-17</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/accessibility/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/attribution/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/get-involved/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/privacy/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/providers/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/services/providers/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/services/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
|
||||
|
||||
<url>
|
||||
<loc>https://spoonfairies.help/team/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
</urlset>
|
||||
+767
@@ -0,0 +1,767 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
|
||||
|
||||
<title>team | Spoonfairies</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Meta -->
|
||||
<link rel="canonical" href="/">
|
||||
<meta name="description" content="find the services you need from others who need you">
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta property="og:title" content="team | Spoonfairies">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://spoonfairies.help/team/">
|
||||
<meta property="og:description" content="find the services you need from others who need you">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:image" content="https://spoonfairies.help/favicon.ico">
|
||||
<meta property="og:image:alt" content="the Spoonfairies logo, a spoon with wings">
|
||||
|
||||
<meta name="generator" content="Eleventy v3.1.5">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- CSS -->
|
||||
|
||||
|
||||
<style>:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
--font-family: "Atkinson Hyperlegible Next", sans-serif;
|
||||
|
||||
/* Theme colors */
|
||||
--color-light: #fbe8fd;
|
||||
--color-dark: #110526;
|
||||
|
||||
--color-pink-dark: #741189;
|
||||
--color-pink-light: #d972ef;
|
||||
--color-purple-dark: #421397;
|
||||
--color-purple-light: #9d6ff4;
|
||||
--color-blue-dark: #1243a7;
|
||||
--color-blue-light: #6586ea;
|
||||
|
||||
/* light/dark mode resolved colors */
|
||||
--color-bg: light-dark(var(--color-light), var(--color-dark));
|
||||
--color-text: light-dark(var(--color-dark), var(--color-light));
|
||||
|
||||
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
|
||||
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
|
||||
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
|
||||
}
|
||||
|
||||
/* numbered components are from https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box; /* 1. Use a more-intuitive box-sizing model */
|
||||
}
|
||||
|
||||
#root, #__next {
|
||||
isolation: isolate; /* 10. Create a root stacking context */
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords; /* 3. Enable keyword animations */
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
font-family: var(--font-family);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 40rem;
|
||||
line-height: 1.5; /* 4. Increase line-height */
|
||||
-webkit-font-smoothing: antialiased; /* 5. Improve text rendering */
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section + section {
|
||||
margin-top: 4rem;;
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block; /* 6. Improve media defaults */
|
||||
max-width: 100%; /* 6. Improve media defaults */
|
||||
height: auto; /* 6. Improve media defaults */
|
||||
}
|
||||
|
||||
input, button, textarea, select {
|
||||
font: inherit; /* 7. Inherit fonts for form controls */
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word; /* 8. Avoid text overflows */
|
||||
}
|
||||
|
||||
p {
|
||||
text-wrap: pretty; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
p + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
text-wrap: balance; /* 9. Improve line wrapping */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
padding-bottom: .1rem;
|
||||
border-bottom: .1rem solid var(--color-pink);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: .05rem;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.long-items li + li {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text);
|
||||
text-decoration-thickness: .2rem;
|
||||
text-decoration-color: var(--color-blue);
|
||||
border-radius: .1rem;
|
||||
outline-offset: .1rem;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration-color: var(--color-purple);
|
||||
}
|
||||
a:hover,
|
||||
a:active {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
a:focus-visible {
|
||||
text-decoration: none;
|
||||
outline: .15rem solid var(--color-pink);
|
||||
}
|
||||
a.ha {
|
||||
color: var(--color-pink);
|
||||
outline-offset: 0;
|
||||
}
|
||||
span.ha-placeholder {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
.visually-hidden:not(:focus):not(:active) {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* name coloration */
|
||||
.brand {
|
||||
color: var(--color-pink);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* provider pronouns */
|
||||
.pronouns::after {
|
||||
content: ")" / "";
|
||||
}
|
||||
.pronouns::before {
|
||||
content: "(pronouns: " / "pronouns: ";
|
||||
}
|
||||
.pronouns {
|
||||
color: var(--color-text);
|
||||
opacity: .75;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
/* location spans */
|
||||
.location::before {
|
||||
content: "📍" / "is based out of ";
|
||||
}
|
||||
.online::before {
|
||||
content: "🌐" / "is ";
|
||||
}
|
||||
.location,
|
||||
.online {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Posts list */
|
||||
.providerlist {
|
||||
padding: 0;
|
||||
}
|
||||
.providerlist-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.providerlist-item + .providerlist-item {
|
||||
border-top: .1rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.providerlist-link {
|
||||
font-size: 1.35rem; /* 19px /16 */
|
||||
font-weight: 700;
|
||||
flex-basis: 100%;
|
||||
border-bottom: .05rem solid var(--color-pink);
|
||||
text-decoration: none;
|
||||
color: var(--color-blue);
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
column-gap: .5rem;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.providerlist-link:visited {
|
||||
color: var(--color-purple);
|
||||
}
|
||||
.providerlist-link:hover,
|
||||
.providerlist-link:active {
|
||||
color: var(--color-pink);
|
||||
}
|
||||
.providerlist-link:focus-visible {
|
||||
border-bottom-color: var(--color-bg);
|
||||
}
|
||||
.providerlist-link .location,
|
||||
.providerlist-link .online {
|
||||
flex-grow: 5;
|
||||
}
|
||||
.providerlist-item-active .providerlist-link {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.post-tag {
|
||||
display: inline-block;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tags list */
|
||||
.taglist {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: .5rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* related providers section on provider pages */
|
||||
.related-providers {
|
||||
border-top: .1rem dotted;
|
||||
padding-top: 1rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* services page */
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding-left: 0;
|
||||
column-gap: 1rem;
|
||||
row-gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
.service {
|
||||
list-style: none;
|
||||
}
|
||||
.service a {
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
.service a:focus-visible {
|
||||
border-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/* post metadata */
|
||||
.post-metadata {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
gap: .5rem;
|
||||
}
|
||||
.post-time {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
margin: 0;
|
||||
}
|
||||
@media (max-width: 500px) {
|
||||
.post-metadata .taglist {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
/* provider contact info */
|
||||
.contacts {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
.contacts li {
|
||||
list-style: none;
|
||||
}
|
||||
.contacts li + li {
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
/* team page specific */
|
||||
.team-contact {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
header {
|
||||
border-bottom: .2rem dotted;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#skip-link {
|
||||
text-decoration: none;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
padding: 0.5rem 1rem;
|
||||
outline-offset: 0;
|
||||
border-radius: .25rem;
|
||||
}
|
||||
/* Prevent visually-hidden skip link fom pushing content around when focused */
|
||||
#skip-link.visually-hidden:focus {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
/* Ensure it is positioned on top of everything else when it is shown */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* logo and title */
|
||||
.home-link {
|
||||
font-weight: 700;
|
||||
border-radius: .25rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
outline-offset: .25rem;
|
||||
text-underline-offset: .2rem;
|
||||
}
|
||||
.home-link:link:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
a[href].home-link:focus-visible {
|
||||
outline-width: .25rem;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 3rem !important;
|
||||
width: 3rem !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
header h2 {
|
||||
margin: 0;
|
||||
outline-offset: .25rem;
|
||||
border: none;
|
||||
text-transform: lowercase;
|
||||
color: var(--color-pink);
|
||||
}
|
||||
header a:has(h2):hover {
|
||||
text-decoration-color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: .5rem 1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav a[href][aria-current="page"] {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: .2rem;
|
||||
}
|
||||
.nav a[href][aria-current="page"]:hover {
|
||||
text-decoration-color: var(--color-pink);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 2.25rem;
|
||||
padding-top: 2rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
.pagination li {
|
||||
list-style: none;
|
||||
}
|
||||
.pagination li:not(:has(a)),
|
||||
.pagination a {
|
||||
display: block;
|
||||
border: .1rem dotted;
|
||||
padding: .5rem;
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: .2rem dotted;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
footer .year {
|
||||
width: 30%;
|
||||
padding-top: .5rem;
|
||||
margin: 1rem auto;
|
||||
border-top: .05rem solid;
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.footer-links {
|
||||
flex-flow: column;
|
||||
gap: .1rem;
|
||||
align-items: center;
|
||||
}
|
||||
footer .year {
|
||||
width: 65%;
|
||||
}
|
||||
}</style>
|
||||
|
||||
<!-- JS -->
|
||||
|
||||
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
|
||||
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
|
||||
let globalInstanceIndex = 0;
|
||||
|
||||
class HeadingAnchors extends HTMLElement {
|
||||
static register(tagName = "heading-anchors", registry = window.customElements) {
|
||||
if(registry && !registry.get(tagName)) {
|
||||
registry.define(tagName, this);
|
||||
}
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
exclude: "data-ha-exclude",
|
||||
prefix: "prefix",
|
||||
content: "content",
|
||||
}
|
||||
|
||||
static classes = {
|
||||
anchor: "ha",
|
||||
placeholder: "ha-placeholder",
|
||||
srOnly: "ha-visualhide",
|
||||
}
|
||||
|
||||
static defaultSelector = "h2,h3,h4,h5,h6";
|
||||
|
||||
static css = `
|
||||
.${HeadingAnchors.classes.srOnly} {
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: var(--ha_offsetx);
|
||||
top: var(--ha_offsety);
|
||||
text-decoration: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
opacity: .3;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
|
||||
opacity: 1;
|
||||
}
|
||||
.${HeadingAnchors.classes.anchor},
|
||||
.${HeadingAnchors.classes.placeholder} {
|
||||
display: inline-block;
|
||||
padding: 0 .25em;
|
||||
|
||||
/* Disable selection of visually hidden label */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@supports (anchor-name: none) {
|
||||
.${HeadingAnchors.classes.anchor} {
|
||||
position: absolute;
|
||||
left: anchor(left);
|
||||
top: anchor(top);
|
||||
}
|
||||
}`;
|
||||
|
||||
get supports() {
|
||||
return "replaceSync" in CSSStyleSheet.prototype;
|
||||
}
|
||||
|
||||
get supportsAnchorPosition() {
|
||||
return CSS.supports("anchor-name: none");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(HeadingAnchors.css);
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
||||
|
||||
this.headingStyles = {};
|
||||
this.instanceIndex = globalInstanceIndex++;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.supports) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.headings.forEach((heading, index) => {
|
||||
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
|
||||
let anchor = this.getAnchorElement(heading);
|
||||
let placeholder = this.getPlaceholderElement();
|
||||
|
||||
// Prefers anchor position approach for better accessibility
|
||||
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
|
||||
if(this.supportsAnchorPosition) {
|
||||
let anchorName = `--ha_${this.instanceIndex}_${index}`;
|
||||
placeholder.style.setProperty("anchor-name", anchorName);
|
||||
anchor.style.positionAnchor = anchorName;
|
||||
}
|
||||
|
||||
heading.appendChild(placeholder);
|
||||
heading.after(anchor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchorFromPlaceholder(placeholder) {
|
||||
if(!placeholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
|
||||
if(!heading.nextElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO next element could be more defensive
|
||||
this.positionAnchor(heading.nextElementSibling);
|
||||
}
|
||||
|
||||
// Polyfill-only
|
||||
positionAnchor(anchor) {
|
||||
if(!anchor || !anchor.previousElementSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO previous element could be more defensive
|
||||
let heading = anchor.previousElementSibling;
|
||||
this.setFontProp(heading, anchor);
|
||||
|
||||
if(this.supportsAnchorPosition) {
|
||||
// quit early
|
||||
return;
|
||||
}
|
||||
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
|
||||
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
|
||||
}
|
||||
}
|
||||
|
||||
setFontProp(heading, anchor) {
|
||||
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
let style = getComputedStyle(placeholder);
|
||||
let props = ["font-weight", "font-size", "line-height", "font-family"];
|
||||
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
|
||||
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
|
||||
let vars = style.getPropertyValue("font-variation-settings");
|
||||
if(vars) {
|
||||
anchor.style.setProperty("font-variation-settings", vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccessibleTextPrefix() {
|
||||
// Useful for i18n
|
||||
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
|
||||
}
|
||||
|
||||
getContent() {
|
||||
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
|
||||
return this.getAttribute(HeadingAnchors.attributes.content);
|
||||
}
|
||||
return "#";
|
||||
}
|
||||
|
||||
// Placeholder nests inside of heading
|
||||
getPlaceholderElement() {
|
||||
let ph = document.createElement("span");
|
||||
ph.setAttribute("aria-hidden", true);
|
||||
ph.classList.add(HeadingAnchors.classes.placeholder);
|
||||
let content = this.getContent();
|
||||
if(content) {
|
||||
ph.textContent = content;
|
||||
}
|
||||
|
||||
ph.addEventListener("mouseover", (e) => {
|
||||
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
|
||||
if(placeholder) {
|
||||
this.positionAnchorFromPlaceholder(placeholder);
|
||||
}
|
||||
});
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
getAnchorElement(heading) {
|
||||
let anchor = document.createElement("a");
|
||||
anchor.href = `#${heading.id}`;
|
||||
anchor.classList.add(HeadingAnchors.classes.anchor);
|
||||
|
||||
let content = this.getContent();
|
||||
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
|
||||
|
||||
anchor.addEventListener("focus", e => {
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
if(anchor) {
|
||||
this.positionAnchor(anchor);
|
||||
}
|
||||
});
|
||||
|
||||
anchor.addEventListener("mouseover", (e) => {
|
||||
// when CSS anchor positioning is supported, this is only used to set the font
|
||||
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
|
||||
this.positionAnchor(anchor);
|
||||
});
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
get headings() {
|
||||
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
|
||||
}
|
||||
|
||||
get selector() {
|
||||
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
|
||||
}
|
||||
}
|
||||
|
||||
HeadingAnchors.register();
|
||||
|
||||
export { HeadingAnchors }</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" class="home-link" title="home page">
|
||||
<img src="/.11ty/image/?src=src%2Flogo.jpg&width=585&format=jpeg&via=transform" alt="the Spoonfairies logo, showing a spoon with fairy wings" loading="lazy" decoding="async" width="585" height="585">
|
||||
<h2>Spoonfairies</h2>
|
||||
</a>
|
||||
|
||||
<nav aria-label="main navigation">
|
||||
<ul class="nav">
|
||||
<li class="nav-item"><a href="/providers/">all providers</a></li>
|
||||
<li class="nav-item"><a href="/services/">all services</a></li>
|
||||
<li class="nav-item"><a href="/get-involved/">get involved</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
<main id="main">
|
||||
<h1>our team</h1>
|
||||
<section>
|
||||
<h2 id="core">core</h2>
|
||||
<h3 id="organizer-bex">organizer Bex</h3>
|
||||
<p>Bex is an educator, social worker, and artist committed to creating a better world for queer and disabled people. Vey have been a Seattle renter since the early 2010s, dreaming and scheming more liberated futures for us all, while knitting socks and admiring the cutest kitties on the planet.</p>
|
||||
<p><a class="team-contact" href="mailto:info@spoonfairies.help?subject=Spoonfairies">email Bex about <span class="brand">Spoonfairies</span></a></p>
|
||||
<h3 id="developer-lee">developer Lee</h3>
|
||||
<p>Lee is a multidisciplinary artist and software engineer who works across many media. In the artistic space, ze creates block prints, leatherwork, handspun yarn, knits, and more; technologically, hir skills range from front-end web development with a focus on accessibility to back-end infrastructure and devOps. You can find Lee online at <a href="https://leecat.art" target="_blank" rel="external">leecat.art</a>.</p>
|
||||
<p><a class="team-contact" href="mailto:tech@spoonfairies.help?subject=Spoonfairies">email Lee about <span class="brand">Spoonfairies</span></a></p>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-links">
|
||||
<p><a href="/attribution" title="site attribution">attribution</a></p>
|
||||
<p><a href="/team" title="meet or contact the Spoonfairies team">team</a></p>
|
||||
<p><a href="/privacy" title="data privacy">privacy</a></p>
|
||||
<p><a href="/accessibility" title="accessibility statements">accessibility</a></p>
|
||||
<p><a href="https://heckin.technology/inherentlee/spoonfairies" target="_blank" title="source code">source</a></p>
|
||||
</div>
|
||||
<p class="year"><a href="/" title="home page">Spoonfairies 2026</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- This page `/team/` was built on 2026-09-23T23:25:06.281Z -->
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user