annot.at is a service for syncing static websites, such as blogs, to atproto and standard.site annot.at
elixir atproto standardsite
3

Configure Feed

Select the types of activity you want to include in your feed.

Apply landing page design

I experimented a bit with prototypes and this is just lifting the design into the Phoenix app. Could probably extract some things into components.

Johanna Larsson (Jun 17, 2026, 9:02 PM +0100) 0558b520 0b10e4c1

+373 -169
+74 -63
assets/css/app.css
··· 1 1 /* See the Tailwind configuration guide for advanced usage 2 - https://tailwindcss.com/docs/configuration */ 2 + https://tailwindcss.com/docs/configuration */ 3 3 4 4 @import "tailwindcss" source(none); 5 5 @source "../css"; ··· 7 7 @source "../../lib/annot_at_web"; 8 8 9 9 /* A Tailwind plugin that makes "hero-#{ICON}" classes available. 10 - The heroicons installation itself is managed by your mix.exs */ 10 + The heroicons installation itself is managed by your mix.exs */ 11 11 @plugin "../vendor/heroicons"; 12 12 13 - /* daisyUI Tailwind Plugin. You can update this file by fetching the latest version with: 14 - curl -sLO https://github.com/saadeghi/daisyui/releases/latest/download/daisyui.js 15 - Make sure to look at the daisyUI changelog: https://daisyui.com/docs/changelog/ */ 13 + /* daisyUI Tailwind Plugin. */ 16 14 @plugin "../vendor/daisyui" { 17 15 themes: false; 18 16 } 19 17 20 - /* daisyUI theme plugin. You can update this file by fetching the latest version with: 21 - curl -sLO https://github.com/saadeghi/daisyui/releases/latest/download/daisyui-theme.js 22 - We ship with two themes, a light one inspired on Phoenix colors and a dark one inspired 23 - on Elixir colors. Build your own at: https://daisyui.com/theme-generator/ */ 24 - @plugin "../vendor/daisyui-theme" { 25 - name: "dark"; 26 - default: false; 27 - prefersdark: true; 28 - color-scheme: "dark"; 29 - --color-base-100: oklch(30.33% 0.016 252.42); 30 - --color-base-200: oklch(25.26% 0.014 253.1); 31 - --color-base-300: oklch(20.15% 0.012 254.09); 32 - --color-base-content: oklch(97.807% 0.029 256.847); 33 - --color-primary: oklch(58% 0.233 277.117); 34 - --color-primary-content: oklch(96% 0.018 272.314); 35 - --color-secondary: oklch(58% 0.233 277.117); 36 - --color-secondary-content: oklch(96% 0.018 272.314); 37 - --color-accent: oklch(60% 0.25 292.717); 38 - --color-accent-content: oklch(96% 0.016 293.756); 39 - --color-neutral: oklch(37% 0.044 257.287); 40 - --color-neutral-content: oklch(98% 0.003 247.858); 41 - --color-info: oklch(58% 0.158 241.966); 42 - --color-info-content: oklch(97% 0.013 236.62); 43 - --color-success: oklch(60% 0.118 184.704); 44 - --color-success-content: oklch(98% 0.014 180.72); 45 - --color-warning: oklch(66% 0.179 58.318); 46 - --color-warning-content: oklch(98% 0.022 95.277); 47 - --color-error: oklch(58% 0.253 17.585); 48 - --color-error-content: oklch(96% 0.015 12.422); 49 - --radius-selector: 0.25rem; 50 - --radius-field: 0.25rem; 51 - --radius-box: 0.5rem; 52 - --size-selector: 0.21875rem; 53 - --size-field: 0.21875rem; 54 - --border: 1.5px; 55 - --depth: 1; 56 - --noise: 0; 18 + /* Self-hosted variable fonts (latin, weight axis). Syne has no italic axis, 19 + so italic renders as a synthesized oblique, matching the prototype. */ 20 + @font-face { 21 + font-family: "Syne"; 22 + font-style: normal; 23 + font-display: swap; 24 + font-weight: 400 800; 25 + src: url("/fonts/syne-latin-wght-normal.woff2") format("woff2"); 26 + } 27 + 28 + @font-face { 29 + font-family: "Plus Jakarta Sans"; 30 + font-style: normal; 31 + font-display: swap; 32 + font-weight: 200 800; 33 + src: url("/fonts/plus-jakarta-sans-latin-wght-normal.woff2") format("woff2"); 34 + } 35 + 36 + /* Brand theme: source of truth for the palette + the utilities the landing 37 + page uses (bg-paper, text-ink, bg-sky-bold, font-display, animate-float). */ 38 + @theme { 39 + --color-paper: #fdfcfb; 40 + --color-ink: #1a1a1a; 41 + --color-sky-light: #e0f2fe; 42 + --color-sky-bold: #7dd3fc; 43 + --color-peach-light: #fecaca; 44 + --color-peach-bold: #f9a8a8; 45 + 46 + --font-sans: "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif; 47 + --font-display: "Syne", ui-sans-serif, system-ui, sans-serif; 48 + 49 + --animate-float: float 6s ease-in-out infinite; 50 + --animate-entrance: entrance 0.8s ease-out both; 51 + 52 + @keyframes float { 53 + 0%, 100% { transform: translateY(0) rotate(-1deg); } 54 + 50% { transform: translateY(-8px) rotate(1deg); } 55 + } 56 + 57 + @keyframes entrance { 58 + 0% { opacity: 0; transform: translateY(20px) rotate(-2deg); } 59 + 100% { opacity: 1; transform: translateY(0) rotate(0); } 60 + } 61 + } 62 + 63 + /* Respect reduced-motion preferences. */ 64 + @media (prefers-reduced-motion: reduce) { 65 + .animate-float, 66 + .animate-entrance { 67 + animation: none; 68 + } 57 69 } 58 70 71 + /* daisyUI light theme, recolored to the brand palette so forms and flashes 72 + stay consistent with the landing page. */ 59 73 @plugin "../vendor/daisyui-theme" { 60 74 name: "light"; 61 75 default: true; 62 76 prefersdark: false; 63 77 color-scheme: "light"; 64 - --color-base-100: oklch(98% 0 0); 65 - --color-base-200: oklch(96% 0.001 286.375); 66 - --color-base-300: oklch(92% 0.004 286.32); 67 - --color-base-content: oklch(21% 0.006 285.885); 68 - --color-primary: oklch(70% 0.213 47.604); 69 - --color-primary-content: oklch(98% 0.016 73.684); 70 - --color-secondary: oklch(55% 0.027 264.364); 71 - --color-secondary-content: oklch(98% 0.002 247.839); 72 - --color-accent: oklch(0% 0 0); 73 - --color-accent-content: oklch(100% 0 0); 74 - --color-neutral: oklch(44% 0.017 285.786); 75 - --color-neutral-content: oklch(98% 0 0); 76 - --color-info: oklch(62% 0.214 259.815); 77 - --color-info-content: oklch(97% 0.014 254.604); 78 - --color-success: oklch(70% 0.14 182.503); 78 + --color-base-100: #fdfcfb; 79 + --color-base-200: #f6f4f1; 80 + --color-base-300: #eae6e1; 81 + --color-base-content: #1a1a1a; 82 + --color-primary: #1a1a1a; 83 + --color-primary-content: #fdfcfb; 84 + --color-secondary: #f9a8a8; 85 + --color-secondary-content: #1a1a1a; 86 + --color-accent: #7dd3fc; 87 + --color-accent-content: #1a1a1a; 88 + --color-neutral: #1a1a1a; 89 + --color-neutral-content: #fdfcfb; 90 + --color-info: oklch(58% 0.158 241.966); 91 + --color-info-content: oklch(97% 0.013 236.62); 92 + --color-success: oklch(60% 0.118 184.704); 79 93 --color-success-content: oklch(98% 0.014 180.72); 80 94 --color-warning: oklch(66% 0.179 58.318); 81 95 --color-warning-content: oklch(98% 0.022 95.277); 82 96 --color-error: oklch(58% 0.253 17.585); 83 97 --color-error-content: oklch(96% 0.015 12.422); 84 - --radius-selector: 0.25rem; 85 - --radius-field: 0.25rem; 86 - --radius-box: 0.5rem; 98 + --radius-selector: 0.625rem; 99 + --radius-field: 0.625rem; 100 + --radius-box: 1rem; 87 101 --size-selector: 0.21875rem; 88 102 --size-field: 0.21875rem; 89 103 --border: 1.5px; ··· 95 109 @custom-variant phx-click-loading (.phx-click-loading&, .phx-click-loading &); 96 110 @custom-variant phx-submit-loading (.phx-submit-loading&, .phx-submit-loading &); 97 111 @custom-variant phx-change-loading (.phx-change-loading&, .phx-change-loading &); 98 - 99 - /* Use the data attribute for dark mode */ 100 - @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *)); 101 112 102 113 /* Make LiveView wrapper divs transparent for layout */ 103 114 [data-phx-session], [data-phx-teleported-src] { display: contents }
+3 -69
lib/annot_at_web/components/layouts.ex
··· 35 35 36 36 def app(assigns) do 37 37 ~H""" 38 - <header class="navbar px-4 sm:px-6 lg:px-8"> 39 - <div class="flex-1"> 40 - <a href="/" class="flex-1 flex w-fit items-center gap-2"> 41 - <img src={~p"/images/logo.svg"} width="36" /> 42 - <span class="text-sm font-semibold">v{Application.spec(:phoenix, :vsn)}</span> 43 - </a> 44 - </div> 45 - <div class="flex-none"> 46 - <ul class="flex flex-column px-1 space-x-4 items-center"> 47 - <li> 48 - <a href="https://phoenixframework.org/" class="btn btn-ghost">Website</a> 49 - </li> 50 - <li> 51 - <a href="https://github.com/phoenixframework/phoenix" class="btn btn-ghost">GitHub</a> 52 - </li> 53 - <li> 54 - <.theme_toggle /> 55 - </li> 56 - <li> 57 - <a href="https://hexdocs.pm/phoenix/overview.html" class="btn btn-primary"> 58 - Get Started <span aria-hidden="true">&rarr;</span> 59 - </a> 60 - </li> 61 - </ul> 62 - </div> 63 - </header> 64 - 65 - <main class="px-4 py-20 sm:px-6 lg:px-8"> 66 - <div class="mx-auto max-w-2xl space-y-4"> 67 - {render_slot(@inner_block)} 68 - </div> 69 - </main> 38 + <div class="min-h-screen bg-paper text-ink antialiased"> 39 + {render_slot(@inner_block)} 40 + </div> 70 41 71 42 <.flash_group flash={@flash} /> 72 43 """ ··· 111 82 {gettext("Attempting to reconnect")} 112 83 <.icon name="hero-arrow-path" class="ml-1 size-3 motion-safe:animate-spin" /> 113 84 </.flash> 114 - </div> 115 - """ 116 - end 117 - 118 - @doc """ 119 - Provides dark vs light theme toggle based on themes defined in app.css. 120 - 121 - See <head> in root.html.heex which applies the theme before page load. 122 - """ 123 - def theme_toggle(assigns) do 124 - ~H""" 125 - <div class="card relative flex flex-row items-center border-2 border-base-300 bg-base-300 rounded-full"> 126 - <div class="absolute w-1/3 h-full rounded-full border-1 border-base-200 bg-base-100 brightness-200 left-0 [[data-theme=light]_&]:left-1/3 [[data-theme=dark]_&]:left-2/3 transition-[left]" /> 127 - 128 - <button 129 - class="flex p-2 cursor-pointer w-1/3" 130 - phx-click={JS.dispatch("phx:set-theme")} 131 - data-phx-theme="system" 132 - > 133 - <.icon name="hero-computer-desktop-micro" class="size-4 opacity-75 hover:opacity-100" /> 134 - </button> 135 - 136 - <button 137 - class="flex p-2 cursor-pointer w-1/3" 138 - phx-click={JS.dispatch("phx:set-theme")} 139 - data-phx-theme="light" 140 - > 141 - <.icon name="hero-sun-micro" class="size-4 opacity-75 hover:opacity-100" /> 142 - </button> 143 - 144 - <button 145 - class="flex p-2 cursor-pointer w-1/3" 146 - phx-click={JS.dispatch("phx:set-theme")} 147 - data-phx-theme="dark" 148 - > 149 - <.icon name="hero-moon-micro" class="size-4 opacity-75 hover:opacity-100" /> 150 - </button> 151 85 </div> 152 86 """ 153 87 end
+4 -21
lib/annot_at_web/components/layouts/root.html.heex
··· 1 1 <!DOCTYPE html> 2 - <html lang="en"> 2 + <html lang="en" data-theme="light"> 3 3 <head> 4 4 <meta charset="utf-8" /> 5 5 <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 6 <meta name="csrf-token" content={get_csrf_token()} /> 7 - <.live_title default="AnnotAt" suffix=" · Phoenix Framework" phx-no-format>{assigns[:page_title]}</.live_title> 7 + <.live_title default="annot.at · publish your blog to atproto" suffix=" · annot.at"> 8 + {assigns[:page_title]} 9 + </.live_title> 8 10 <link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} /> 9 11 <script defer phx-track-static type="text/javascript" src={~p"/assets/js/app.js"}> 10 - </script> 11 - <script> 12 - (() => { 13 - const setTheme = (theme) => { 14 - if (theme === "system") { 15 - localStorage.removeItem("phx:theme"); 16 - document.documentElement.removeAttribute("data-theme"); 17 - } else { 18 - localStorage.setItem("phx:theme", theme); 19 - document.documentElement.setAttribute("data-theme", theme); 20 - } 21 - }; 22 - if (!document.documentElement.hasAttribute("data-theme")) { 23 - setTheme(localStorage.getItem("phx:theme") || "system"); 24 - } 25 - window.addEventListener("storage", (e) => e.key === "phx:theme" && setTheme(e.newValue || "system")); 26 - 27 - window.addEventListener("phx:set-theme", (e) => setTheme(e.target.dataset.phxTheme)); 28 - })(); 29 12 </script> 30 13 </head> 31 14 <body>
+292 -16
lib/annot_at_web/controllers/page_html/home.html.heex
··· 1 - <Layouts.app flash={@flash} current_scope={@current_scope}> 2 - <div class="mx-auto max-w-md space-y-6 py-16 text-center"> 3 - <h1 class="text-3xl font-bold">annot.at</h1> 1 + <Layouts.app flash={@flash}> 2 + <div id="top" class="font-sans"> 3 + <%!-- Nav --%> 4 + <nav class="sticky top-0 z-50 border-b border-ink/5 bg-paper/80 backdrop-blur-md"> 5 + <div class="mx-auto flex max-w-6xl items-center justify-between px-6 py-4"> 6 + <a href="#top" class="font-display text-xl font-bold tracking-tight">annot.at</a> 7 + <div class="flex items-center gap-6"> 8 + <a href="#how" class="hidden text-sm text-ink/70 transition hover:text-ink sm:inline"> 9 + How it works 10 + </a> 11 + <a 12 + href="#features" 13 + class="hidden text-sm text-ink/70 transition hover:text-ink sm:inline" 14 + > 15 + Features 16 + </a> 17 + <a href="#faq" class="hidden text-sm text-ink/70 transition hover:text-ink sm:inline"> 18 + FAQ 19 + </a> 20 + <.link 21 + href={~p"/login"} 22 + class="rounded-xl bg-ink px-4 py-2 text-sm font-bold text-paper transition-all hover:scale-[1.02] active:scale-[0.98]" 23 + > 24 + Sign in 25 + </.link> 26 + </div> 27 + </div> 28 + </nav> 29 + 30 + <main class="mx-auto max-w-6xl px-6"> 31 + <%!-- Hero --%> 32 + <section class="grid grid-cols-1 items-center gap-12 py-16 lg:grid-cols-12 lg:py-24"> 33 + <div class="animate-entrance lg:col-span-7"> 34 + <span class="inline-block rounded-full bg-peach-light px-3 py-1 text-xs font-semibold uppercase tracking-widest text-ink"> 35 + standard.site lexicon 36 + </span> 37 + <h1 class="mt-6 font-display text-5xl font-bold leading-[0.9] tracking-tight sm:text-6xl lg:text-8xl"> 38 + Your blog,<br /> 39 + <span class="italic text-sky-bold">published</span> <br /> to the ATmosphere. 40 + </h1> 41 + <p class="mt-8 max-w-xl text-lg leading-relaxed text-ink/70"> 42 + annot.at watches your RSS feed and publishes every new post to atproto 43 + using the standard.site lexicon, so readers across Leaflet, pckt.blog, 44 + Offprint, and any other ATmosphere reader can find it. 45 + </p> 46 + <div class="mt-10 flex flex-wrap gap-4"> 47 + <.link 48 + href={~p"/login"} 49 + class="rounded-2xl bg-ink px-8 py-4 text-lg font-bold text-paper shadow-xl shadow-ink/10 transition-all hover:scale-[1.02] active:scale-[0.98]" 50 + > 51 + Sign in with atproto 52 + </.link> 53 + <a 54 + href="https://tangled.sh" 55 + class="rounded-2xl border-2 border-ink/10 px-8 py-4 text-lg font-bold text-ink transition-colors hover:bg-sky-light/30" 56 + > 57 + View on tangled.sh 58 + </a> 59 + </div> 60 + </div> 4 61 5 - <%= if @current_scope do %> 6 - <%= if @current_scope.user.avatar_url do %> 7 - <img src={@current_scope.user.avatar_url} alt="" class="mx-auto h-20 w-20 rounded-full" /> 8 - <% end %> 9 - <p class="text-lg font-semibold"> 10 - {@current_scope.user.display_name || @current_scope.user.handle} 11 - </p> 12 - <p class="opacity-80">{@current_scope.user.handle}</p> 13 - <p class="text-sm opacity-60">{@current_scope.user.did}</p> 14 - <.link href={~p"/logout"} method="delete" class="underline">Log out</.link> 15 - <% else %> 16 - <.link navigate={~p"/login"} class="underline">Sign in with Bluesky</.link> 17 - <% end %> 62 + <div class="animate-float lg:col-span-5"> 63 + <div class="rounded-[2.5rem_1.5rem_4rem_2rem] border-2 border-ink bg-sky-light p-6 shadow-[12px_12px_0px_0px_var(--color-sky-bold)]"> 64 + <div class="mb-4 flex items-center justify-between"> 65 + <div class="flex items-center gap-2"> 66 + <div class="h-7 w-7 rounded-full border border-ink/20 bg-peach-bold"></div> 67 + <div class="font-mono text-[11px] leading-tight text-ink/70"> 68 + <div class="font-bold text-ink">field notes</div> 69 + <div>@fieldnotes.example.com</div> 70 + </div> 71 + </div> 72 + <span class="rotate-[4deg] rounded-full border border-ink/10 bg-peach-bold px-2 py-1 text-[9px] font-bold uppercase tracking-widest text-ink"> 73 + live 74 + </span> 75 + </div> 76 + 77 + <div class="mb-3 font-mono text-[10px] uppercase tracking-widest text-ink/40"> 78 + site.standard.document · 3 recent 79 + </div> 80 + 81 + <div class="space-y-2"> 82 + <article 83 + :for={ 84 + {title, desc, time} <- [ 85 + {"Notes on writing in public", 86 + "Why I started publishing the rough drafts too.", "just now"}, 87 + {"A small garden of ideas", "Tending a personal wiki across the ATmosphere.", 88 + "2d ago"}, 89 + {"RSS is still the best protocol", 90 + "And how annot.at quietly bridges it to atproto.", "1w ago"} 91 + ] 92 + } 93 + class="rounded-xl border border-ink/10 bg-paper p-3" 94 + > 95 + <div class="text-sm font-semibold text-ink">{title}</div> 96 + <div class="mt-0.5 text-xs text-ink/60">{desc}</div> 97 + <div class="mt-2 flex items-center justify-between font-mono text-[10px] text-ink/40"> 98 + <span>published to atproto</span> 99 + <span>{time}</span> 100 + </div> 101 + </article> 102 + </div> 103 + </div> 104 + </div> 105 + </section> 106 + 107 + <%!-- Bluesky previews --%> 108 + <section class="py-8 lg:py-12"> 109 + <div class="flex flex-col items-start gap-8 rounded-[1.5rem] bg-ink p-8 text-paper lg:flex-row lg:items-center lg:gap-12 lg:p-12"> 110 + <div class="lg:flex-1"> 111 + <h2 class="font-display text-3xl font-bold tracking-tight sm:text-4xl"> 112 + Shared anywhere, signed by you. 113 + </h2> 114 + <p class="mt-4 max-w-md leading-relaxed text-paper/70"> 115 + Post a link on Bluesky and it reads your standard.site records, turning a bare URL 116 + into a branded preview with your publication and a button straight back to your site. 117 + </p> 118 + </div> 119 + 120 + <div class="flex w-full flex-col gap-3 rounded-2xl border-2 border-ink bg-paper p-3 text-ink shadow-[6px_6px_0px_0px_var(--color-sky-bold)] lg:w-auto lg:flex-row lg:items-center"> 121 + <div class="flex min-w-0 items-center gap-3"> 122 + <div class="grid h-10 w-10 flex-none place-items-center rounded-xl border border-ink/15 bg-peach-bold font-display text-lg font-bold"> 123 + F 124 + </div> 125 + <div class="min-w-0"> 126 + <div class="font-bold leading-tight">Field Notes</div> 127 + <div class="truncate text-sm text-ink/60">by @fieldnotes.example.com</div> 128 + </div> 129 + </div> 130 + <span class="flex items-center justify-center gap-1 whitespace-nowrap rounded-full bg-ink px-4 py-2 text-sm font-bold text-paper lg:ml-auto"> 131 + View publication 132 + <.icon name="hero-arrow-up-right" class="size-4" /> 133 + </span> 134 + </div> 135 + 136 + 137 + </div> 138 + </section> 139 + 140 + 141 + <%!-- Steps --%> 142 + <section id="how" class="py-16 lg:py-24"> 143 + <div class="text-center"> 144 + <h2 class="font-display text-4xl font-bold tracking-tight sm:text-5xl"> 145 + Three steps to reach the ATmosphere 146 + </h2> 147 + <p class="mt-3 text-ink/60">Minimal setup. No extra writing.</p> 148 + </div> 149 + 150 + <div class="mt-12 grid grid-cols-1 gap-6 md:grid-cols-3 items-start"> 151 + <div class="rounded-2xl border border-ink/10 bg-peach-light p-8 shadow-sm transition-transform md:rotate-[-1deg] hover:rotate-0"> 152 + <div class="font-display text-4xl font-bold text-ink/25">01</div> 153 + <h3 class="mt-4 text-xl font-bold">Connect your atproto account</h3> 154 + <p class="mt-3 text-sm leading-relaxed text-ink/70"> 155 + Sign in once with your handle (any PDS, Bluesky included). We only ask 156 + permission to write standard.site records on your behalf. 157 + </p> 158 + </div> 159 + <div class="rounded-2xl border border-ink/10 bg-sky-light p-8 shadow-sm transition-transform md:mt-8 md:rotate-[1.5deg] hover:rotate-0"> 160 + <div class="font-display text-4xl font-bold text-ink/25">02</div> 161 + <h3 class="mt-4 text-xl font-bold">Point us at your blog</h3> 162 + <p class="mt-3 text-sm leading-relaxed text-ink/70"> 163 + Register your publication: RSS or Atom URL plus the canonical URL of 164 + your blog. We create your site.standard.publication record. 165 + </p> 166 + </div> 167 + <div class="rounded-2xl border border-ink/10 bg-paper p-8 shadow-sm transition-transform md:rotate-[-0.5deg] hover:rotate-0"> 168 + <div class="font-display text-4xl font-bold text-ink/25">03</div> 169 + <h3 class="mt-4 text-xl font-bold">Write as usual</h3> 170 + <p class="mt-3 text-sm leading-relaxed text-ink/70"> 171 + Every time your feed updates, we publish a site.standard.document to 172 + your PDS so it lands in standard.site readers across the ATmosphere. 173 + </p> 174 + </div> 175 + </div> 176 + </section> 177 + 178 + <%!-- Features --%> 179 + <section id="features" class="py-16 lg:py-24"> 180 + <div class="grid grid-cols-1 gap-6 md:grid-cols-4"> 181 + <div class="rounded-[1.5rem] bg-ink p-8 text-paper md:col-span-2"> 182 + <h3 class="font-display text-3xl font-bold">We watch your RSS.</h3> 183 + <p class="mt-4 leading-relaxed text-paper/70"> 184 + annot.at polls your feed every few minutes. New posts become atproto 185 + records on your PDS within minutes of going live on your blog. 186 + </p> 187 + </div> 188 + <div class="rounded-[1.5rem] bg-peach-bold p-8 text-ink"> 189 + <h3 class="font-display text-xl font-bold">standard.site native</h3> 190 + <p class="mt-3 text-sm leading-relaxed text-ink/80"> 191 + Built on the community lexicons maintained by the Leaflet, pckt.blog, 192 + and Offprint teams. No custom schema, no lock-in. 193 + </p> 194 + </div> 195 + <div class="rounded-[1.5rem] bg-sky-bold p-8 text-ink"> 196 + <h3 class="font-display text-xl font-bold">Free for basics, forever</h3> 197 + <p class="mt-3 text-sm leading-relaxed text-ink/80"> 198 + annot.at's reason to exist is just to support the community, personal blogs, and 199 + small communities. Self-host or use our hosted service for free. 200 + </p> 201 + </div> 202 + </div> 203 + 204 + <div class="mt-6 flex flex-col items-start justify-between gap-6 rounded-[1.5rem] border border-ink/10 p-8 md:flex-row md:items-center"> 205 + <div> 206 + <h3 class="font-display text-xl font-bold">Built in public.</h3> 207 + <p class="mt-2 text-sm text-ink/70"> 208 + annot.at is open source. Self-host it on a small VPS if you'd rather run it yourself. 209 + </p> 210 + </div> 211 + <div class="flex flex-wrap gap-3"> 212 + <a 213 + href="#" 214 + class="rounded-xl border border-ink bg-paper px-6 py-3 text-sm font-bold transition-colors hover:bg-peach-light" 215 + > 216 + Docs 217 + </a> 218 + <a 219 + href="https://github.com/joladev/annot.at" 220 + class="rounded-xl border border-ink bg-paper px-6 py-3 text-sm font-bold transition-colors hover:bg-peach-light" 221 + > 222 + GitHub 223 + </a> 224 + <a 225 + href="https://tangled.org/jola.dev/annot.at" 226 + class="rounded-xl border border-ink bg-paper px-6 py-3 text-sm font-bold transition-colors hover:bg-peach-light" 227 + > 228 + tangled.sh 229 + </a> 230 + </div> 231 + </div> 232 + </section> 233 + 234 + <%!-- FAQ --%> 235 + <section id="faq" class="mx-auto max-w-3xl py-16 lg:py-24"> 236 + <h2 class="text-center font-display text-4xl font-bold tracking-tight sm:text-5xl"> 237 + Questions? 238 + </h2> 239 + <div class="mt-10"> 240 + <details 241 + :for={ 242 + {q, a} <- [ 243 + {"What is standard.site?", 244 + "A set of open AT Protocol lexicons for publications and documents. Publishing to them turns your posts into portable records that any ATmosphere reader, like Leaflet or pckt.blog, can pick up."}, 245 + {"Will my posts show up on Bluesky?", 246 + "Not automatically. annot.at writes standard.site records to your repo. Bluesky cross-posting is a separate, opt-in step you can enable later."}, 247 + {"Which RSS feeds work?", 248 + "Any standard RSS or Atom feed. Point us at your feed URL plus your blog's canonical URL and we handle the rest."}, 249 + {"Do I keep ownership of my posts?", 250 + "Yes. Records live in your own PDS under your DID. annot.at only holds permission to write them, and you can revoke it or self-host anytime."}, 251 + {"Can I self-host?", 252 + "Yes. annot.at is open source. Run it on a small VPS if you'd rather operate it yourself."} 253 + ] 254 + } 255 + class="group border-b border-ink/10 py-5" 256 + > 257 + <summary class="flex cursor-pointer list-none items-center justify-between text-lg font-semibold [&::-webkit-details-marker]:hidden"> 258 + {q} 259 + <span class="text-2xl text-ink/40 transition-transform group-open:rotate-45"> 260 + + 261 + </span> 262 + </summary> 263 + <p class="mt-3 text-ink/70">{a}</p> 264 + </details> 265 + </div> 266 + </section> 267 + </main> 268 + 269 + <%!-- Footer --%> 270 + <footer class="border-t border-ink/10"> 271 + <div class="mx-auto flex max-w-6xl flex-col items-center justify-between gap-4 px-6 py-12 sm:flex-row"> 272 + <span class="font-display text-lg font-bold">annot.at</span> 273 + <div class="flex flex-wrap items-center justify-center gap-6 text-sm text-ink/60"> 274 + <a 275 + href="https://tangled.org/jola.dev/annot.at/blob/main/README.md" 276 + class="transition hover:text-ink" 277 + > 278 + Docs 279 + </a> 280 + <a href="https://github.com/joladev/annot.at" class="transition hover:text-ink"> 281 + GitHub 282 + </a> 283 + <a href="https://tangled.org/jola.dev/annot.ath" class="transition hover:text-ink"> 284 + tangled.sh 285 + </a> 286 + <a href="https://standard.site" class="transition hover:text-ink">standard.site</a> 287 + <a href="#" class="transition hover:text-ink">Privacy</a> 288 + </div> 289 + <span class="font-mono text-xs uppercase tracking-widest text-ink/40"> 290 + Built in the open for the ATmosphere 291 + </span> 292 + </div> 293 + </footer> 18 294 </div> 19 295 </Layouts.app>
priv/static/fonts/plus-jakarta-sans-latin-wght-normal.woff2

This is a binary file and will not be displayed.

priv/static/fonts/syne-latin-wght-normal.woff2

This is a binary file and will not be displayed.