Let a notebook's design be its own
Two things were drawn on every cover regardless of what was chosen, and between them they are most of why a shelf of notebooks looked alike. A 45-degree stripe texture was baked into the cover at 1.5% white. It was meant to be imperceptible; on the darker gradients it bands visibly, and since covers gained patterns of their own it was a second pattern fighting the first. Removed. And the same pair of rings sat in the middle of every one. The mark belongs to the design now — a ring, a square, a diamond, a dot, a cross, an arc, three bars — and the designs whose pattern already fills the face wear nothing at all. Drawn with borders rather than an image, so each takes its cover's own accent and stays sharp at any size. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
83b76b95a0
commit
e037f01dc2
2 files changed
+125
-29
No files matched your search
@@ -1,5 +1,105 @@
|
||||
import { useState } from "react";
|
||||
import { coverBackground, coverStyle } from "./constants.jsx";
|
||||
import { coverBackground, coverEmblem, coverStyle } from "./constants.jsx";
|
||||
|
||||
/**
|
||||
* The mark in the middle of a cover.
|
||||
*
|
||||
* Every notebook used to wear the same pair of rings, whatever cover was
|
||||
* chosen — which is most of why a shelf of them looked alike. It belongs to the
|
||||
* design now, and the designs whose pattern already fills the face wear nothing.
|
||||
*
|
||||
* Drawn with borders rather than with an image, so it takes the cover's own
|
||||
* accent colour and stays sharp at any size.
|
||||
*/
|
||||
function Emblem({ kind, accent }) {
|
||||
if (kind === "none") return null;
|
||||
|
||||
const box = {
|
||||
position: "absolute",
|
||||
top: 28,
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
width: 50,
|
||||
height: 50,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
};
|
||||
const line = `${accent}55`;
|
||||
const faint = `${accent}33`;
|
||||
|
||||
if (kind === "ring") {
|
||||
return (
|
||||
<div style={{ ...box, borderRadius: "50%", border: `1.5px solid ${accent}44` }}>
|
||||
<div style={{ width: 30, height: 30, borderRadius: "50%", border: `1px solid ${faint}` }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (kind === "square") {
|
||||
return (
|
||||
<div style={{ ...box, border: `1.5px solid ${accent}44`, borderRadius: 3 }}>
|
||||
<div style={{ width: 26, height: 26, border: `1px solid ${faint}`, borderRadius: 2 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (kind === "diamond") {
|
||||
return (
|
||||
<div style={box}>
|
||||
<div
|
||||
style={{
|
||||
width: 34,
|
||||
height: 34,
|
||||
border: `1.5px solid ${accent}44`,
|
||||
transform: "rotate(45deg)",
|
||||
borderRadius: 3,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (kind === "dot") {
|
||||
return (
|
||||
<div style={box}>
|
||||
<div style={{ width: 16, height: 16, borderRadius: "50%", background: line }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (kind === "cross") {
|
||||
return (
|
||||
<div style={{ ...box, flexDirection: "column", gap: 0 }}>
|
||||
<div style={{ width: 40, height: 1.5, background: line }} />
|
||||
<div style={{ width: 1.5, height: 40, background: line, marginTop: -20.75, marginBottom: -20.75 }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (kind === "arc") {
|
||||
return (
|
||||
<div style={box}>
|
||||
<div
|
||||
style={{
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: "50%",
|
||||
borderTop: `1.5px solid ${accent}55`,
|
||||
borderRight: `1.5px solid ${accent}22`,
|
||||
borderBottom: "1.5px solid transparent",
|
||||
borderLeft: `1.5px solid ${accent}22`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (kind === "bars") {
|
||||
return (
|
||||
<div style={{ ...box, gap: 5 }}>
|
||||
{[18, 30, 22].map((h, i) => (
|
||||
<div key={i} style={{ width: 2, height: h, borderRadius: 1, background: line }} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* ═══════ NOTEBOOK COVER ═══════ */
|
||||
export function NotebookCover({
|
||||
@@ -19,6 +119,7 @@ export function NotebookCover({
|
||||
// colour, the face from both.
|
||||
const cover = coverStyle(notebook.cover, notebook.coverPattern);
|
||||
const face = coverBackground(notebook.cover, notebook.coverPattern);
|
||||
const emblem = coverEmblem(notebook.cover, notebook.coverPattern);
|
||||
const [hovered, setHovered] = useState(false);
|
||||
return (
|
||||
<button
|
||||
@@ -126,14 +227,6 @@ export function NotebookCover({
|
||||
zIndex: 3,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
background:
|
||||
"repeating-linear-gradient(45deg,transparent,transparent 30px,rgba(255,255,255,0.015) 30px,rgba(255,255,255,0.015) 60px)",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
@@ -145,25 +238,7 @@ export function NotebookCover({
|
||||
opacity: 0.7,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 28,
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
width: 50,
|
||||
height: 50,
|
||||
borderRadius: "50%",
|
||||
border: `1.5px solid ${cover.accent}44`,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{ width: 30, height: 30, borderRadius: "50%", border: `1px solid ${cover.accent}33` }}
|
||||
/>
|
||||
</div>
|
||||
<Emblem kind={emblem} accent={cover.accent} />
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
|
||||
@@ -65,57 +65,67 @@ export const DEFAULT_FOLDERS = [
|
||||
* two crossed diagonals meant as a chevron came out as noisy plaid at that size.
|
||||
*/
|
||||
export const PATTERNS = [
|
||||
{ id: "plain", label: "Plain", layers: "" },
|
||||
{ emblem: "ring", id: "plain", label: "Plain", layers: "" },
|
||||
{
|
||||
id: "ruled",
|
||||
emblem: "none",
|
||||
label: "Ruled",
|
||||
layers: "repeating-linear-gradient(0deg,rgba(255,255,255,.12) 0 1px,transparent 1px 17px)",
|
||||
},
|
||||
{
|
||||
id: "grid",
|
||||
emblem: "square",
|
||||
label: "Grid",
|
||||
layers:
|
||||
"repeating-linear-gradient(0deg,rgba(255,255,255,.10) 0 1px,transparent 1px 20px), repeating-linear-gradient(90deg,rgba(255,255,255,.10) 0 1px,transparent 1px 20px)",
|
||||
},
|
||||
{
|
||||
id: "pinstripe",
|
||||
emblem: "diamond",
|
||||
label: "Pinstripe",
|
||||
layers: "repeating-linear-gradient(45deg,rgba(255,255,255,.09) 0 6px,transparent 6px 14px)",
|
||||
},
|
||||
{
|
||||
id: "bands",
|
||||
emblem: "none",
|
||||
label: "Bands",
|
||||
layers: "repeating-linear-gradient(0deg,rgba(0,0,0,.18) 0 13px,transparent 13px 26px)",
|
||||
},
|
||||
{
|
||||
id: "dots",
|
||||
emblem: "dot",
|
||||
label: "Dots",
|
||||
layers: "radial-gradient(rgba(255,255,255,.18) 1.4px,transparent 1.5px) 0 0/13px 13px",
|
||||
},
|
||||
{
|
||||
id: "hatch",
|
||||
emblem: "cross",
|
||||
label: "Cross-hatch",
|
||||
layers:
|
||||
"repeating-linear-gradient(45deg,rgba(255,255,255,.08) 0 1px,transparent 1px 11px), repeating-linear-gradient(-45deg,rgba(255,255,255,.08) 0 1px,transparent 1px 11px)",
|
||||
},
|
||||
{
|
||||
id: "linen",
|
||||
emblem: "arc",
|
||||
label: "Linen",
|
||||
layers:
|
||||
"repeating-linear-gradient(0deg,rgba(255,255,255,.07) 0 1px,transparent 1px 4px), repeating-linear-gradient(90deg,rgba(0,0,0,.07) 0 1px,transparent 1px 4px)",
|
||||
},
|
||||
{
|
||||
id: "ledger",
|
||||
emblem: "bars",
|
||||
label: "Ledger",
|
||||
layers: "repeating-linear-gradient(90deg,rgba(255,255,255,.09) 0 1px,transparent 1px 26px)",
|
||||
},
|
||||
{
|
||||
id: "glow",
|
||||
emblem: "none",
|
||||
label: "Corner light",
|
||||
layers: "radial-gradient(120% 85% at 18% 0%,rgba(255,255,255,.20),transparent 62%)",
|
||||
},
|
||||
{
|
||||
id: "foot",
|
||||
emblem: "square",
|
||||
label: "Foot band",
|
||||
layers: "linear-gradient(0deg,rgba(0,0,0,.34) 0 22%,transparent 22%)",
|
||||
},
|
||||
@@ -149,5 +159,16 @@ export function coverBackground(cover, pattern) {
|
||||
return layers ? `${layers}, ${base.bg}` : base.bg;
|
||||
}
|
||||
|
||||
/**
|
||||
* The mark in the middle of a cover.
|
||||
*
|
||||
* It used to be the same pair of rings on every notebook, whatever cover was
|
||||
* chosen — which, with the same title frame and the same three dots, is why a
|
||||
* shelf of them all looked alike. It belongs to the design now: some wear a
|
||||
* ring, some a diamond, and the ones whose pattern already fills the face wear
|
||||
* nothing at all.
|
||||
*/
|
||||
export const coverEmblem = (cover, pattern) => PATTERNS[coverParts(cover, pattern).pattern]?.emblem || "none";
|
||||
|
||||
/** The rest of a cover's colours — the spine, and the accent used beside it. */
|
||||
export const coverStyle = (cover, pattern) => COVERS[coverParts(cover, pattern).cover];
|
||||
Reference in new issue
Block a user