-
{React.string("Posts")}
-
- {React.string("Data fetched from the mock GraphQL server via Relay.")}
-
+
+ {React.string("Recent Writing")}
+
}>
{React.string("Loading posts...")} }>
+ fallback={ {React.string("Loading...")} }>
diff --git a/src/styles/GlobalStyles.res b/src/styles/GlobalStyles.res
index 215cad8..752c382 100644
--- a/src/styles/GlobalStyles.res
+++ b/src/styles/GlobalStyles.res
@@ -18,9 +18,9 @@ let injectGlobalStyles = () => {
body {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
- font-size: 1rem;
+ font-size: 1.0625rem;
font-weight: 400;
- line-height: 1.5;
+ line-height: 1.75;
color: #ece4d8;
background-color: #1a1816;
min-height: 100vh;
@@ -30,18 +30,20 @@ let injectGlobalStyles = () => {
/* Typography Elements */
h1, h2, h3, h4, h5, h6 {
+ font-family: 'Playfair Display', Georgia, 'Times New Roman', serif;
color: #ece4d8;
- font-weight: 600;
- line-height: 1.25;
+ font-weight: 400;
+ line-height: 1.2;
+ letter-spacing: -0.02em;
margin-bottom: 1rem;
}
- h1 { font-size: 2.25rem; font-weight: 700; }
- h2 { font-size: 1.875rem; }
+ h1 { font-size: 2.5rem; }
+ h2 { font-size: 2rem; }
h3 { font-size: 1.5rem; }
h4 { font-size: 1.25rem; }
h5 { font-size: 1.125rem; }
- h6 { font-size: 1rem; font-weight: 500; }
+ h6 { font-size: 1rem; }
p {
margin-bottom: 1rem;
@@ -59,6 +61,7 @@ let injectGlobalStyles = () => {
a:hover {
color: #dfc088;
text-decoration: underline;
+ text-underline-offset: 3px;
}
a:focus {
diff --git a/src/styles/Theme.res b/src/styles/Theme.res
index 72d73c0..bcbe404 100644
--- a/src/styles/Theme.res
+++ b/src/styles/Theme.res
@@ -85,6 +85,7 @@ module Theme = {
module Typography = {
let fontFamily = {
"sans": "Inter, Avenir, Helvetica, Arial, sans-serif",
+ "serif": "'Playfair Display', Georgia, 'Times New Roman', serif",
"mono": "'Fira Code', 'Monaco', 'Cascadia Code', monospace",
}
From a4056f99cc000127eceffb4b7580274940932178 Mon Sep 17 00:00:00 2001
From: Antonette Caldwell <18711313+acald-creator@users.noreply.github.com>
Date: Mon, 13 Apr 2026 20:16:29 -0500
Subject: [PATCH 2/2] feat(components): add Divider, Avatar, Badge, and
Blockquote
Add editorial components: Divider with optional centered label,
Avatar with initials fallback and 3 sizes, Badge with 5 semantic
variants, and Blockquote with serif italic styling and attribution.
Add showcase sections to Components page.
---
src/components/Avatar/Avatar.res | 61 ++++++++++++++++++++++++
src/components/Badge/Badge.res | 54 +++++++++++++++++++++
src/components/Blockquote/Blockquote.res | 41 ++++++++++++++++
src/components/Divider/Divider.res | 44 +++++++++++++++++
src/pages/ComponentsPage.res | 49 +++++++++++++++++++
5 files changed, 249 insertions(+)
create mode 100644 src/components/Avatar/Avatar.res
create mode 100644 src/components/Badge/Badge.res
create mode 100644 src/components/Blockquote/Blockquote.res
create mode 100644 src/components/Divider/Divider.res
diff --git a/src/components/Avatar/Avatar.res b/src/components/Avatar/Avatar.res
new file mode 100644
index 0000000..31d6686
--- /dev/null
+++ b/src/components/Avatar/Avatar.res
@@ -0,0 +1,61 @@
+open Emotion.Css
+open Emotion.Utils
+
+module Avatar = {
+ type size = [#sm | #md | #lg]
+
+ @react.component
+ let make = (~name: string, ~src: option
=?, ~size: size=#md, ~className: string="", ()) => {
+ let dimension = switch size {
+ | #sm => "2rem"
+ | #md => "2.5rem"
+ | #lg => "3.5rem"
+ }
+
+ let fontSize = switch size {
+ | #sm => "0.75rem"
+ | #md => "0.875rem"
+ | #lg => "1.125rem"
+ }
+
+ let baseStyles = css({
+ "width": dimension,
+ "height": dimension,
+ "borderRadius": "50%",
+ "display": "inline-flex",
+ "alignItems": "center",
+ "justifyContent": "center",
+ "flexShrink": "0",
+ "overflow": "hidden",
+ "backgroundColor": Color.bgElevated,
+ "border": `1px solid ${Color.border}`,
+ "color": Color.textSecondary,
+ "fontSize": fontSize,
+ "fontWeight": "500",
+ "letterSpacing": "0.05em",
+ "textTransform": "uppercase",
+ })
+
+ let imgStyles = css({
+ "width": "100%",
+ "height": "100%",
+ "objectFit": "cover",
+ })
+
+ let initials =
+ name
+ ->String.split(" ")
+ ->Array.filterMap(part => part->String.charAt(0)->Some)
+ ->Array.slice(~start=0, ~end=2)
+ ->Array.join("")
+
+ let avatarClassName = cx([baseStyles, className])
+
+
+ {switch src {
+ | Some(url) =>

+ | None => React.string(initials)
+ }}
+
+ }
+}
diff --git a/src/components/Badge/Badge.res b/src/components/Badge/Badge.res
new file mode 100644
index 0000000..50d3332
--- /dev/null
+++ b/src/components/Badge/Badge.res
@@ -0,0 +1,54 @@
+open Emotion.Css
+open Emotion.Utils
+
+module Badge = {
+ type variant = [#default | #primary | #success | #warning | #error]
+
+ @react.component
+ let make = (~variant: variant=#default, ~className: string="", ~children, ()) => {
+ let baseStyles = css({
+ "display": "inline-flex",
+ "alignItems": "center",
+ "fontFamily": Theme.Theme.Typography.fontFamily["sans"],
+ "fontSize": "0.6875rem",
+ "fontWeight": "500",
+ "letterSpacing": "0.05em",
+ "textTransform": "uppercase",
+ "padding": "0.25rem 0.625rem",
+ "borderRadius": "2px",
+ "lineHeight": "1.25",
+ })
+
+ let variantStyles = switch variant {
+ | #default => css({
+ "backgroundColor": Color.bgElevated,
+ "color": Color.textSecondary,
+ "border": `1px solid ${Color.border}`,
+ })
+ | #primary => css({
+ "backgroundColor": `${Color.primary}1a`,
+ "color": Color.primary,
+ "border": `1px solid ${Color.primary}33`,
+ })
+ | #success => css({
+ "backgroundColor": "rgba(34, 197, 94, 0.1)",
+ "color": Color.success,
+ "border": "1px solid rgba(34, 197, 94, 0.2)",
+ })
+ | #warning => css({
+ "backgroundColor": "rgba(245, 158, 11, 0.1)",
+ "color": Color.warning,
+ "border": "1px solid rgba(245, 158, 11, 0.2)",
+ })
+ | #error => css({
+ "backgroundColor": "rgba(239, 68, 68, 0.1)",
+ "color": Color.error,
+ "border": "1px solid rgba(239, 68, 68, 0.2)",
+ })
+ }
+
+ let badgeClassName = cx([baseStyles, variantStyles, className])
+
+ {children}
+ }
+}
diff --git a/src/components/Blockquote/Blockquote.res b/src/components/Blockquote/Blockquote.res
new file mode 100644
index 0000000..b9b582a
--- /dev/null
+++ b/src/components/Blockquote/Blockquote.res
@@ -0,0 +1,41 @@
+open Emotion.Css
+open Emotion.Utils
+
+module Blockquote = {
+ @react.component
+ let make = (~cite: option=?, ~className: string="", ~children, ()) => {
+ let quoteStyles = css({
+ "fontFamily": Theme.Theme.Typography.fontFamily["serif"],
+ "fontSize": "1.375rem",
+ "fontWeight": "400",
+ "fontStyle": "italic",
+ "lineHeight": "1.6",
+ "color": Color.textPrimary,
+ "borderLeft": `3px solid ${Color.primary}`,
+ "paddingLeft": "1.5rem",
+ "margin": "2rem 0",
+ })
+
+ let citeStyles = css({
+ "display": "block",
+ "marginTop": "0.75rem",
+ "fontFamily": Theme.Theme.Typography.fontFamily["sans"],
+ "fontSize": "0.8125rem",
+ "fontStyle": "normal",
+ "fontWeight": "400",
+ "letterSpacing": "0.05em",
+ "color": Color.textSecondary,
+ })
+
+ let quoteClassName = cx([quoteStyles, className])
+
+
+ {children}
+ {switch cite {
+ | Some(attribution) =>
+ {React.string({`\u2014 `} ++ attribution)}
+ | None => React.null
+ }}
+
+ }
+}
diff --git a/src/components/Divider/Divider.res b/src/components/Divider/Divider.res
new file mode 100644
index 0000000..759dc9b
--- /dev/null
+++ b/src/components/Divider/Divider.res
@@ -0,0 +1,44 @@
+open Emotion.Css
+open Emotion.Utils
+
+module Divider = {
+ @react.component
+ let make = (~label: option=?, ~className: string="", ()) => {
+ let baseStyles = css({
+ "display": "flex",
+ "alignItems": "center",
+ "gap": "1rem",
+ "margin": "2rem 0",
+ })
+
+ let lineStyles = css({
+ "flex": "1",
+ "height": "1px",
+ "backgroundColor": Color.border,
+ })
+
+ let labelStyles = css({
+ "fontFamily": Theme.Theme.Typography.fontFamily["sans"],
+ "fontSize": "0.6875rem",
+ "fontWeight": "500",
+ "letterSpacing": "0.15em",
+ "textTransform": "uppercase",
+ "color": Color.textSecondary,
+ })
+
+ let dividerClassName = cx([baseStyles, className])
+
+ switch label {
+ | Some(text) =>
+
+
+
{React.string(text)}
+
+
+ | None =>
+
+ }
+ }
+}
diff --git a/src/pages/ComponentsPage.res b/src/pages/ComponentsPage.res
index 4812866..6983370 100644
--- a/src/pages/ComponentsPage.res
+++ b/src/pages/ComponentsPage.res
@@ -5,6 +5,10 @@ module Components = {
module Input = Input.Input
module Card = Card.Card
module Typography = Typography.Typography
+ module Divider = Divider.Divider
+ module Avatar = Avatar.Avatar
+ module Badge = Badge.Badge
+ module Blockquote = Blockquote.Blockquote
}
@react.component
@@ -116,5 +120,50 @@ let make = () => {
+