diff --git a/src/web/tailwind.css b/src/web/tailwind.css index d5b08e6..7326bbc 100644 --- a/src/web/tailwind.css +++ b/src/web/tailwind.css @@ -85,11 +85,12 @@ @layer components { /* ---------- shared controls ---------- */ - .btn, .button { @apply inline-flex min-h-10 items-center justify-center rounded-sm border border-rule-strong bg-transparent px-3 py-1.5 font-sans text-sm font-medium text-ink no-underline transition-colors duration-150 hover:bg-ink hover:text-paper focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-paper; } - .btn-primary { @apply inline-flex min-h-10 items-center justify-center rounded-sm border border-ink bg-ink px-3 py-1.5 font-sans text-sm font-medium text-paper no-underline transition-colors duration-150 hover:border-accent hover:bg-accent hover:text-paper focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-paper; } + .btn, .button { @apply inline-flex min-h-10 items-center justify-center rounded-sm border border-rule-strong bg-transparent px-3 py-1.5 font-sans text-sm font-medium text-ink no-underline duration-150 hover:bg-ink hover:text-paper focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-paper; transition-property:transform, background-color, color, border-color; } + .btn-primary { @apply inline-flex min-h-10 items-center justify-center rounded-sm border border-ink bg-ink px-3 py-1.5 font-sans text-sm font-medium text-paper no-underline duration-150 hover:border-accent hover:bg-accent hover:text-paper focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-paper; transition-property:transform, background-color, color, border-color; } .btn-quiet { @apply inline-flex min-h-9 items-center gap-1 rounded-sm border-0 bg-transparent px-2 py-1 font-sans text-sm text-muted underline decoration-rule underline-offset-4 hover:text-ink hover:decoration-accent; } .btn:disabled, .btn-primary:disabled, .btn-danger:disabled { @apply cursor-not-allowed border-rule bg-paper-2 text-muted hover:bg-paper-2 hover:text-muted; } - .btn-danger, button.danger { @apply inline-flex min-h-9 items-center justify-center rounded-sm border border-down bg-transparent px-2.5 py-1 font-sans text-sm font-medium text-down transition-colors duration-150 hover:bg-down hover:text-paper; } + .btn-danger, button.danger { @apply inline-flex min-h-9 items-center justify-center rounded-sm border border-down bg-transparent px-2.5 py-1 font-sans text-sm font-medium text-down duration-150 hover:bg-down hover:text-paper; transition-property:transform, background-color, color, border-color; } + .btn:active, .button:active, .btn-primary:active, .btn-danger:active, button.danger:active { transform:scale(0.96); } .badge { @apply inline-flex items-center whitespace-nowrap rounded-full px-2 py-0.5 font-sans text-xs font-medium leading-5; color:var(--ink-2); background:color-mix(in oklab, var(--ink) 8%, transparent); } .badge.selected, .badge.loved, .badge.ok { color:var(--loved); background:color-mix(in oklab, var(--loved) 14%, transparent); } @@ -107,10 +108,13 @@ .prose-body { @apply text-[1.0625rem] leading-[1.72] text-ink; } .prose-body > * + * { @apply mt-5; } - .prose-body h2 { @apply mt-10 border-t border-rule pt-4 text-2xl font-semibold leading-[1.15] tracking-[-0.01em]; } - .prose-body h3 { @apply mt-8 text-xl font-semibold leading-tight; } + .prose-body p, .index-summary { text-wrap:pretty; } + .prose-body h2 { @apply mt-10 border-t border-rule pt-4 text-2xl font-semibold leading-[1.1] tracking-[-0.01em]; } + .prose-body h3 { @apply mt-8 text-xl font-semibold leading-[1.15] tracking-[-0.01em]; } .prose-body a { @apply text-ink underline decoration-rule underline-offset-4 hover:text-ink hover:decoration-accent; } - .prose-body img { @apply mx-auto my-7 block h-auto max-w-full rounded-sm text-center font-sans text-xs italic text-muted; } + .prose-body img { @apply mx-auto my-7 block h-auto max-w-full rounded-sm text-center font-sans text-xs italic text-muted; outline:1px solid oklch(0 0 0 / 0.1); outline-offset:-1px; } + @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .prose-body img { outline-color:oklch(1 0 0 / 0.1); } } + :root[data-theme="dark"] .prose-body img { outline-color:oklch(1 0 0 / 0.1); } .prose-body pre { @apply max-w-full overflow-x-auto rounded-sm bg-paper-2 p-4 text-sm leading-relaxed; } .prose-body :not(pre) > code { @apply rounded-sm bg-paper-2 px-1 py-0.5 text-[0.9em]; } .prose-body blockquote { @apply my-7 border-l-2 border-rule-strong pl-5 text-ink-2; } @@ -119,14 +123,27 @@ .prose-body li::marker { color:var(--muted); } .prose-body li + li { @apply mt-2; } - .rating { @apply my-5 flex flex-wrap items-center font-sans text-sm; } - .rating-prompt { @apply mr-3 text-[0.72rem] uppercase tracking-[0.1em] text-muted; } - .rating button[data-label]:not(.clear) { @apply -ml-px min-h-10 rounded-none border-rule bg-transparent px-3 py-1.5 text-ink first:ml-0 first:rounded-l-sm last:rounded-r-sm hover:z-10 hover:border-ink hover:bg-paper-2; } - .rating button[data-label]:not(.clear).active { @apply z-10 border-ink bg-ink text-paper; } - .rating button.clear { @apply ml-2 min-h-10 border-0 bg-transparent px-2 text-muted underline decoration-rule underline-offset-4 hover:text-ink; } - .rating button.clear.active { @apply font-semibold text-ink; } - .rating-note { @apply mt-2 flex w-full max-w-md basis-full items-center gap-2 text-muted; } - .rating-note input { @apply flex-1; } + .reader-page :where(h1, h2, h3, h4), .reader-section-heading { text-wrap:balance; } + .reader-section-heading { @apply border-t-2 border-rule-strong pt-3 font-serif text-xl font-semibold leading-[1.1] tracking-[-0.01em] text-ink; } + + .rating { @apply my-5 flex flex-wrap items-center gap-x-3 font-sans text-xs; } + .rating-prompt { @apply font-serif text-[0.8rem] italic text-muted; } + .rating-actions { @apply flex min-h-10 items-center whitespace-nowrap; } + .rating button[data-label] { @apply relative h-8 text-xs duration-150; transition-property:transform, background-color, color, border-color; } + .rating button[data-label]::after { content:""; @apply absolute -inset-y-1 inset-x-0; } + .rating button[data-label]:active { transform:scale(0.96); } + .rating button[data-label]:not(.clear) { @apply -ml-px rounded-none border-rule bg-transparent px-2 py-1 text-ink first:ml-0 first:rounded-l-sm last:rounded-r-sm hover:z-10 hover:border-ink hover:bg-paper-2; } + .rating button.active[data-label="loved"] { @apply z-10 border-loved text-loved; background:color-mix(in oklab, var(--loved) 14%, transparent); } + .rating button.active[data-label="good"] { @apply z-10 border-good text-good; background:color-mix(in oklab, var(--good) 14%, transparent); } + .rating button.active[data-label="down"] { @apply z-10 border-down text-down; background:color-mix(in oklab, var(--down) 14%, transparent); } + .rating button.clear { @apply invisible ml-1.5 border-0 bg-transparent px-1 text-muted underline decoration-rule underline-offset-4 hover:text-ink; } + .rating:has(button[data-label]:not(.clear).active) button.clear, .rating button.clear.active { @apply visible; } + .rating button.clear.active { @apply no-underline; } + .rating-cleared-label { @apply hidden; } + .rating button.clear.active .rating-clear-label { @apply hidden; } + .rating button.clear.active .rating-cleared-label { @apply inline; } + .rating-note { @apply mt-2 flex w-full max-w-md basis-full items-center gap-2 text-[0.72rem] text-muted; } + .rating-note input { @apply min-w-0 flex-1; } .flash { @apply my-4 border-l-4 border-accent bg-paper-2 px-4 py-3 font-sans text-sm; } .error { @apply border-l-4 border-down bg-paper-2 px-4 py-3 font-sans text-sm text-down; } @@ -195,12 +212,14 @@ tr.superseded a { @apply text-muted; } /* the rating widget has to fit inside a table cell */ - td .rating, .picks .rating { @apply my-1.5 max-w-[15rem] gap-y-1 text-xs; } + td .rating, .picks .rating { @apply my-0 max-w-[15rem] gap-y-0 text-xs; } td .rating-prompt, .picks .rating-prompt { @apply hidden; } - td .rating button[data-label]:not(.clear), .picks .rating button[data-label]:not(.clear) { @apply min-h-8 px-2.5 py-1 text-xs; } - td .rating button.clear, .picks .rating button.clear { @apply ml-2 min-h-8 px-0 text-xs; } - td .rating-note { @apply mt-1 max-w-[15rem] gap-1.5 text-[0.7rem]; } - td .rating-note input { @apply px-1.5 py-0.5 text-xs; } + td .rating button[data-label]:not(.clear), .picks .rating button[data-label]:not(.clear) { @apply px-2 py-1 text-xs; } + td .rating button.clear, .picks .rating button.clear { @apply ml-1.5 px-0 text-xs; } + td .rating-note { @apply mt-1 max-w-[13rem] gap-0 text-[0.7rem]; } + td .rating-note-label { @apply sr-only; } + td .rating-note input { @apply rounded-none border-x-0 border-t-0 border-b border-transparent bg-transparent px-0 py-1 text-xs shadow-none focus:border-ink focus:ring-0; } + .rating-cell .rating { @apply w-[13rem]; } .picks .rating { @apply max-w-none; } pre.block, pre.preview, pre.journal { @apply overflow-auto whitespace-pre-wrap rounded-sm bg-paper-2 p-3 font-mono text-[0.8125rem] leading-relaxed; overflow-wrap:anywhere; } @@ -266,7 +285,9 @@ } @media (max-width:40rem) { .kv { @apply block; } .kv dt { @apply mt-2; } - .rating { @apply items-stretch; } .rating-prompt, .rating-note { @apply basis-full; } .rating-prompt { @apply mb-2; } + .rating { @apply items-stretch; } .rating-prompt, .rating-note { @apply basis-full; } .rating-prompt { @apply mb-1; } + .rating-actions { @apply min-h-11; } + .rating button[data-label]::after { @apply -inset-y-1.5; } .filters label { @apply w-full; } .filters input, .filters select { @apply w-full; } } diff --git a/src/web/templates/_rating_widget.html b/src/web/templates/_rating_widget.html index d9eaec8..946789f 100644 --- a/src/web/templates/_rating_widget.html +++ b/src/web/templates/_rating_widget.html @@ -3,9 +3,11 @@ Was this a good pick? - - - -{% if widget.show_note %}{% endif %} - + + + + + + +{% if widget.show_note %}{% endif %} diff --git a/src/web/templates/article.html b/src/web/templates/article.html index 80ddf5f..98f97a8 100644 --- a/src/web/templates/article.html +++ b/src/web/templates/article.html @@ -1,5 +1,5 @@ {% extends "layout.html" %}{% block ears %}{{ toc.short_date }}{% endblock %}{% block content %}
{% include "_toc.html" %} -
+

Article

{{ title }}

{% match byline %}{% when Some with (byline) %}

{{ byline }}

{% when None %}{% endmatch %}

{{ meta_line }}

{% match why %}{% when Some with (why) %}

Why it's here: {{ why }}

{% when None %}{% endmatch %}{% match social_line %}{% when Some with (social) %}

{{ social }}

{% when None %}{% endmatch %}{% match summary %}{% when Some with (summary) %}

{{ summary }}

{% when None %}{% endmatch %}{% if excerpt_only %}

Excerpt only — continue reading at the original site.

{% endif %}
{{ body_html|safe }}
{% match discussion_html %}{% when Some with (discussion) %}

Discussion

{{ discussion|safe }}
{% when None %}{% endmatch %} diff --git a/src/web/templates/dashboard/ratings.html b/src/web/templates/dashboard/ratings.html index ef8282e..26bb482 100644 --- a/src/web/templates/dashboard/ratings.html +++ b/src/web/templates/dashboard/ratings.html @@ -62,7 +62,7 @@ VerdictArticleWhen · byNoteAge → decayNeighbour weightFeed creditIn promptIn rebuildUsed last run {% for row in current %} -{{ row.verdict }}{% let widget = row.widget %}{% include "_rating_widget.html" %} +{% let widget = row.widget %}{% include "_rating_widget.html" %} {{ row.title }}
{{ row.feed_title }}{% if !row.issue_date.is_empty() %} · {{ row.issue_date }}{% endif %} {{ row.when }}
{{ row.source }} · {{ row.username }} {{ row.note }} diff --git a/src/web/templates/issue_full.html b/src/web/templates/issue_full.html index 9f1e88e..b4f129b 100644 --- a/src/web/templates/issue_full.html +++ b/src/web/templates/issue_full.html @@ -1,13 +1,13 @@ {% extends "layout.html" %}{% block ears %}{{ toc.short_date }}{% endblock %}{% block content %}
{% include "_toc.html" %} -
+

{{ display_date }} · No. {{ issue_number }}

{{ stats_line }}

-

The Brief

{{ front_page_html|safe }}
+

The Brief

{{ front_page_html|safe }}
{% if !downloads.is_empty() %}
{% for download in downloads %}Download {{ download.label }} {{ download.size }}{% endfor %}
{% endif %}

In This Issue

- {% for section in sections %}

{{ section.name }}

    {% for entry in section.entries %}
  • {{ entry.title }}

    {{ entry.source }} · {{ entry.reading_minutes }} min read

    {% if !entry.summary.is_empty() %}

    {{ entry.summary }}

    {% endif %}{% match entry.why %}{% when Some with (why) %}

    Why it's here: {{ why }}

    {% when None %}{% endmatch %}{% match entry.rating %}{% when Some with (widget) %}{% include "_rating_widget.html" %}{% when None %}{% endmatch %}
  • {% endfor %}
{% endfor %} + {% for section in sections %}

{{ section.name }}

    {% for entry in section.entries %}
  • {{ entry.title }}

    {{ entry.source }} · {{ entry.reading_minutes }} min read

    {% if !entry.summary.is_empty() %}

    {{ entry.summary }}

    {% endif %}{% match entry.why %}{% when Some with (why) %}

    Why it's here: {{ why }}

    {% when None %}{% endmatch %}{% match entry.rating %}{% when Some with (widget) %}{% include "_rating_widget.html" %}{% when None %}{% endmatch %}
  • {% endfor %}
{% endfor %}
{% if has_world || has_behind %}{% endif %} -

Colophon

The Daily EPUB is assembled every morning from a personal feed reader.

Generated
{{ colophon.generated_at }}
Bulk model
{{ colophon.bulk_model }}
Editor model
{{ colophon.editor_model }}
Summaries model
{{ colophon.summaries_model }}
Entries considered
{% match colophon.entries_fetched %}{% when Some with (entries) %}{{ entries }}{% match colophon.feeds_seen %}{% when Some with (feeds) %} from {{ feeds }} feeds{% when None %}{% endmatch %}{% when None %}n/a{% endmatch %}
Candidates scored
{% match colophon.candidates %}{% when Some with (candidates) %}{{ candidates }}{% when None %}n/a{% endmatch %}
Articles selected
{{ colophon.article_count }} across {{ colophon.section_count }} sections
Words
{{ colophon.total_words }} · ~{{ colophon.reading_minutes }} min read
{% for cost in colophon.provider_costs %}
{{ cost.provider }} cost
{{ cost.cost }}
{% endfor %}
Total token cost
{% match colophon.cost_usd %}{% when Some with (cost) %}{{ cost }}{% when None %}n/a{% endmatch %}
Generator
{{ colophon.generator_version }}
+

Colophon

The Daily EPUB is assembled every morning from a personal feed reader.

Generated
{{ colophon.generated_at }}
Bulk model
{{ colophon.bulk_model }}
Editor model
{{ colophon.editor_model }}
Summaries model
{{ colophon.summaries_model }}
Entries considered
{% match colophon.entries_fetched %}{% when Some with (entries) %}{{ entries }}{% match colophon.feeds_seen %}{% when Some with (feeds) %} from {{ feeds }} feeds{% when None %}{% endmatch %}{% when None %}n/a{% endmatch %}
Candidates scored
{% match colophon.candidates %}{% when Some with (candidates) %}{{ candidates }}{% when None %}n/a{% endmatch %}
Articles selected
{{ colophon.article_count }} across {{ colophon.section_count }} sections
Words
{{ colophon.total_words }} · ~{{ colophon.reading_minutes }} min read
{% for cost in colophon.provider_costs %}
{{ cost.provider }} cost
{{ cost.cost }}
{% endfor %}
Total token cost
{% match colophon.cost_usd %}{% when Some with (cost) %}{{ cost }}{% when None %}n/a{% endmatch %}
Generator
{{ colophon.generator_version }}
{% endblock %} diff --git a/src/web/templates/issue_public.html b/src/web/templates/issue_public.html index f925bf3..742f027 100644 --- a/src/web/templates/issue_public.html +++ b/src/web/templates/issue_public.html @@ -1,8 +1,8 @@ {% extends "layout.html" %}{% block ears %}{% if empty %}Morning edition{% else %}{{ issue.short_date }}{% endif %}{% endblock %}{% block content %} -
+
{% if empty %}

Morning edition

No issue yet

The first issue has not been published.

{% else %}

{{ issue.display_date }} · No. {{ issue.issue_number }}

{{ issue.stats_line }}

A personal morning paper, assembled daily; the selection is the reader's, the words are the authors'.

{% if !downloads.is_empty() %}
{% for download in downloads %}{{ download.label }} {{ download.size }}{% endfor %}
{% endif %} - {% for section in issue.sections %}

{{ section.name }}

{% for entry in section.entries %}

{{ entry.title }}

{% match entry.author %}{% when Some with (author) %}{{ author }} · {% when None %}{% endmatch %}{{ entry.source }}{% if !entry.domain.is_empty() %} ({{ entry.domain }}){% endif %} · {{ entry.reading_minutes }} min

{% match entry.summary %}{% when Some with (summary) %}

{{ summary }}

{% when None %}{% endmatch %}{% match entry.why %}{% when Some with (why) %}

Why it's here: {{ why }}

{% when None %}{% endmatch %}{% if !entry.comment_links.is_empty() %}

{% for link in entry.comment_links %}{{ link.label }}{% if !link.meta.is_empty() %}: {{ link.meta }}{% endif %}{% endfor %}

{% endif %}
{% endfor %}
{% endfor %} + {% for section in issue.sections %}

{{ section.name }}

{% for entry in section.entries %}

{{ entry.title }}

{% match entry.author %}{% when Some with (author) %}{{ author }} · {% when None %}{% endmatch %}{{ entry.source }}{% if !entry.domain.is_empty() %} ({{ entry.domain }}){% endif %} · {{ entry.reading_minutes }} min

{% match entry.summary %}{% when Some with (summary) %}

{{ summary }}

{% when None %}{% endmatch %}{% match entry.why %}{% when Some with (why) %}

Why it's here: {{ why }}

{% when None %}{% endmatch %}{% if !entry.comment_links.is_empty() %}

{% for link in entry.comment_links %}{{ link.label }}{% if !link.meta.is_empty() %}: {{ link.meta }}{% endif %}{% endfor %}

{% endif %}
{% endfor %}
{% endfor %}

Browse the archive →

{% endif %}
{% endblock %}