Scale library type up 50%
CI / build-and-deploy (pull_request) Successful in 19s
CI / build-and-deploy (push) Successful in 21s

Every font size in the library design multiplied by 1.5 — card title and
stamp, list title, meta, excerpt and tags, filter tabs, and the
prototype's header.

Geometry had to follow, or the larger type would have broken the layout
it sits in:

- The card grid drops from three columns to two. At 16.5pt in a 95pt
  column a card gets about nine characters per line and every title
  truncates; the text size is what sets the column count.
- Filter tabs grow 30pt -> 42pt tall, the card gains padding and a
  slightly squarer aspect, and the list's color chip, unread dot and tag
  chips scale with the text they sit beside.

Verified in both layouts against the live server. Suite passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgLHztZGaEHvS3KNeGQmRM
This commit is contained in:
Krishna Kumar
2026-07-26 01:54:51 -05:00
co-authored by Claude Opus 5
parent 3c7a783b6c
commit cdbec4aaf8
4 changed files with 48 additions and 45 deletions
+5 -2
View File
@@ -20,8 +20,11 @@ struct LibraryGridView: View {
var body: some View {
ScrollView {
LazyVGrid(
columns: Array(repeating: GridItem(.flexible(), spacing: 8), count: 3),
spacing: 8
// Two columns, not three: at the scaled-up type a third
// column leaves ~9 characters per line and every title
// truncates. The text size sets the column count.
columns: Array(repeating: GridItem(.flexible(), spacing: 10), count: 2),
spacing: 10
) {
ForEach(items) { item in
card(item)
+16 -16
View File
@@ -160,9 +160,9 @@ extension LibraryItem {
}
}
/// Cards give the stamp one 11pt monospaced line, roughly 14 characters.
/// 92 of 300 real domains are longer than that, so drop the subdomain:
/// `toolkit.artlist.io` -> `artlist.io`. The list still shows it in full.
/// Cards give the stamp one monospaced line, roughly 14 characters at the
/// card's width. 92 of 300 real domains are longer than that, so drop the
/// subdomain: `toolkit.artlist.io` -> `artlist.io`. The list shows it full.
static func registered(_ host: String) -> String {
let parts = host.split(separator: ".")
guard parts.count > 2 else { return host }
@@ -249,23 +249,23 @@ struct LibraryCard: View {
let item: LibraryItem
var body: some View {
VStack(alignment: .leading, spacing: 6) {
VStack(alignment: .leading, spacing: 8) {
Text(item.cardDisplay)
.font(.system(size: 11, design: .serif))
.font(.system(size: 16.5, design: .serif))
.foregroundStyle(item.inkOnSwatch)
.multilineTextAlignment(.leading)
.lineLimit(5)
.minimumScaleFactor(0.85)
.minimumScaleFactor(0.8)
Spacer(minLength: 4)
Text(item.stamp)
.font(.system(size: 11, design: .monospaced))
.font(.system(size: 16.5, design: .monospaced))
.foregroundStyle(item.inkOnSwatch)
.lineLimit(1)
.truncationMode(.middle)
}
.padding(9)
.padding(13)
.frame(maxWidth: .infinity, alignment: .topLeading)
.aspectRatio(0.70, contentMode: .fit)
.aspectRatio(0.82, contentMode: .fit)
.background(RoundedRectangle(cornerRadius: 5).fill(item.swatch))
}
}
@@ -304,23 +304,23 @@ struct LibraryTagStrip: View {
}
} label: {
Image(systemName: "plus")
.font(.system(size: 12, weight: .light))
.font(.system(size: 18, weight: .light))
.foregroundStyle(Paper.ink.opacity(0.6))
.frame(width: 38, height: 30)
.frame(width: 50, height: 42)
}
Spacer(minLength: 0)
}
.padding(.horizontal, 18)
}
}
.frame(height: 30)
.frame(height: 42)
}
private func tab(_ filter: LibraryFilter) -> some View {
let active = filter.id == selection
return HStack(spacing: 7) {
Text(filter.name)
.font(.system(size: 11, design: .monospaced))
.font(.system(size: 16.5, design: .monospaced))
.foregroundStyle(active ? Paper.ink : Paper.ink.opacity(0.45))
.lineLimit(1)
if filters.count > 1 {
@@ -328,14 +328,14 @@ struct LibraryTagStrip: View {
withAnimation(.spring(duration: 0.3, bounce: 0)) { close(filter) }
} label: {
Image(systemName: "xmark")
.font(.system(size: 8, weight: .medium))
.font(.system(size: 12, weight: .medium))
.foregroundStyle(Paper.ink.opacity(active ? 0.5 : 0.25))
}
.buttonStyle(.plain)
}
}
.padding(.horizontal, 11)
.frame(height: 30)
.padding(.horizontal, 15)
.frame(height: 42)
.background(alignment: .bottom) {
if active {
// Paper fill sits 0.6pt proud so it erases the strip rule
+13 -13
View File
@@ -27,7 +27,7 @@ struct LibraryListRow: View {
HStack(alignment: .top, spacing: 12) {
RoundedRectangle(cornerRadius: 3)
.fill(item.swatch)
.frame(width: 26, height: 34)
.frame(width: 34, height: 46)
.overlay(alignment: .topLeading) {
if bookmark.unread {
// Ink, not blue: the palette is the only color the
@@ -35,15 +35,15 @@ struct LibraryListRow: View {
// the chip it sits on.
Circle()
.fill(Paper.ink)
.frame(width: 7, height: 7)
.overlay(Circle().stroke(Paper.sheet, lineWidth: 1.5))
.offset(x: -3, y: -3)
.frame(width: 10, height: 10)
.overlay(Circle().stroke(Paper.sheet, lineWidth: 2))
.offset(x: -4, y: -4)
}
}
VStack(alignment: .leading, spacing: 5) {
Text(item.listDisplay)
.font(.system(size: 14, design: .serif))
.font(.system(size: 21, design: .serif))
.foregroundStyle(Paper.ink)
.lineLimit(2)
.fixedSize(horizontal: false, vertical: true)
@@ -55,13 +55,13 @@ struct LibraryListRow: View {
for: bookmark.dateAdded, relativeTo: Date()
))
}
.font(.system(size: 10, design: .monospaced))
.font(.system(size: 15, design: .monospaced))
.foregroundStyle(Paper.ink.opacity(0.45))
.lineLimit(1)
if let excerpt = rowExcerpt {
Text(excerpt.text)
.font(.system(size: 12, design: .serif))
.font(.system(size: 18, design: .serif))
.italic(excerpt.isAI)
.foregroundStyle(Paper.ink.opacity(0.6))
.lineLimit(2)
@@ -80,14 +80,14 @@ struct LibraryListRow: View {
onPodcast()
} label: {
Image(systemName: podcastCached ? "headphones.circle.fill" : "headphones.circle")
.font(.system(size: 17, weight: .light))
.font(.system(size: 25.5, weight: .light))
.foregroundStyle(Paper.ink.opacity(podcastCached ? 0.75 : 0.3))
}
.buttonStyle(.plain)
.sensoryFeedback(.impact(weight: .medium), trigger: podcastTapCount)
}
}
.padding(.vertical, 12)
.padding(.vertical, 14)
.contentShape(Rectangle())
.overlay(alignment: .bottom) {
if readingProgress > 0.02 {
@@ -115,12 +115,12 @@ struct LibraryListRow: View {
HStack(spacing: 5) {
ForEach(effectiveTags, id: \.self) { tag in
Text(tag)
.font(.system(size: 10, design: .monospaced))
.font(.system(size: 15, design: .monospaced))
.foregroundStyle(Paper.ink.opacity(0.55))
.padding(.horizontal, 6)
.padding(.vertical, 2)
.padding(.horizontal, 8)
.padding(.vertical, 3)
.overlay(
RoundedRectangle(cornerRadius: 3)
RoundedRectangle(cornerRadius: 4)
.stroke(Paper.rule.opacity(0.6), lineWidth: 0.6)
)
}