Skip to content

Commit 1892d1d

Browse files
devhimsclaude
andcommitted
sample: redesign + upgrade to Next 16, React 19, Tailwind 4
A real refresh of the demo app — both stack and design. Stack upgrades (security + currency): - youtube-caption-extractor ^1.8.1 → ^1.10.0 - next 14.2.5 → 16.2.6 (Turbopack dev, 220ms cold start) - react / react-dom ^18 → ^19.2.6 - typescript ^5 → ^6.0.3 - tailwindcss 3.4.1 → 4.3.0 (with @tailwindcss/postcss) - @types/* bumped to current - @vercel/analytics added for traffic tracking - tsconfig.json auto-migrated by Next 16 codemod (jsx: preserve → react-jsx) - tailwind.config.ts removed; v4 auto-detects content - globals.css migrated to v4 conventions (@import "tailwindcss") - next.config.mjs gets turbopack.root to silence multi-lockfile warning - next-env.d.ts updated for v16 types - Vulnerability count: 7 (1 critical, 3 high, 3 moderate) → 0 actionable (1 remaining is an npm-audit false positive on a transitive postcss nested inside next; npm's suggested "fix" would downgrade next to 9.x) Visual redesign: - Replaced the dark-glassmorphism template with an editorial, reading- focused aesthetic: warm light paper background, Fraunces serif headline, Inter body, JetBrains Mono accents. No cards, no gradients, no shadows — separation via hairlines and whitespace only. - Single underlined input replaces the boxy input + select + button trio. Arrow icon at the right of the input replaces the explicit submit button. Language picker becomes inline metadata with a dotted underline; "or try" sample IDs sit on the same baseline as the language label. - Headline gets a per-line highlighter-pen effect (linear-gradient with box-decoration-break: clone) so the yellow ink wraps just the text on each line rather than spanning the line box like a selection rectangle. - Roman type within an italic phrase for "YouTube" — the upright + slightly bolder weight against italic surrounds creates emphasis without changing the typeface (classical editorial typography move). - Functional additions: ⌘K to focus search, regex match highlight, full YouTube URL parsing (any of /watch, /shorts, /embed, youtu.be, raw ID), per-segment timestamp links that open the video at that second, .txt and .srt download, copy-to-clipboard, click-to-fetch sample chips. - Proper sticky-footer layout so the footer pins to viewport bottom on empty state and follows content naturally when transcript is present. - suppressHydrationWarning on <body> to silence benign Grammarly / password-manager DOM mutations that previously triggered React's hydration mismatch warning. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 7352849 commit 1892d1d

10 files changed

Lines changed: 1881 additions & 1631 deletions

sample/app/globals.css

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,62 @@
1-
@tailwind base;
2-
@tailwind components;
3-
@tailwind utilities;
1+
@import "tailwindcss";
2+
3+
@theme {
4+
--font-sans: var(--font-sans), ui-sans-serif, system-ui, sans-serif;
5+
--font-serif: var(--font-serif), ui-serif, Georgia, serif;
6+
--font-mono: var(--font-mono), ui-monospace, "SF Mono", Menlo, monospace;
7+
}
48

59
:root {
6-
--foreground-rgb: 0, 0, 0;
7-
--background-start-rgb: 214, 219, 220;
8-
--background-end-rgb: 255, 255, 255;
10+
--paper: #fafaf7;
11+
--ink: #0c0a09;
12+
}
13+
14+
html, body {
15+
background-color: var(--paper);
16+
color: var(--ink);
17+
}
18+
19+
::selection {
20+
background-color: #fde68a;
21+
color: #0c0a09;
922
}
1023

11-
@media (prefers-color-scheme: dark) {
12-
:root {
13-
--foreground-rgb: 255, 255, 255;
14-
--background-start-rgb: 0, 0, 0;
15-
--background-end-rgb: 0, 0, 0;
16-
}
24+
/* Subtle, paper-like scrollbar */
25+
.scrollbar-paper {
26+
scrollbar-width: thin;
27+
scrollbar-color: #d6d3d1 transparent;
28+
}
29+
.scrollbar-paper::-webkit-scrollbar {
30+
width: 10px;
31+
height: 10px;
32+
}
33+
.scrollbar-paper::-webkit-scrollbar-track {
34+
background: transparent;
35+
}
36+
.scrollbar-paper::-webkit-scrollbar-thumb {
37+
background-color: #d6d3d1;
38+
border-radius: 999px;
39+
border: 2px solid transparent;
40+
background-clip: content-box;
41+
}
42+
.scrollbar-paper::-webkit-scrollbar-thumb:hover {
43+
background-color: #a8a29e;
44+
background-clip: content-box;
45+
border: 2px solid transparent;
1746
}
1847

19-
body {
20-
color: rgb(var(--foreground-rgb));
21-
background: linear-gradient(
22-
to bottom,
23-
transparent,
24-
rgb(var(--background-end-rgb))
25-
)
26-
rgb(var(--background-start-rgb));
48+
@utility text-balance {
49+
text-wrap: balance;
2750
}
2851

29-
@layer utilities {
30-
.text-balance {
31-
text-wrap: balance;
32-
}
52+
/* Highlighter-pen effect — wraps only the text per line, sits as a stripe
53+
across the lower portion of the characters. Defined as plain CSS rather
54+
than @utility because Tailwind 4 was tree-shaking it out. */
55+
.highlight {
56+
/* Highlight fully covers the text. Small transparent bands at top and
57+
bottom keep the stripes from butting against adjacent lines on wrap. */
58+
background: linear-gradient(180deg, transparent 15%, #fde68a 15%, #fde68a 94%, transparent 94%);
59+
-webkit-box-decoration-break: clone;
60+
box-decoration-break: clone;
61+
padding: 0 0.08em;
3362
}

sample/app/layout.tsx

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,45 @@
1-
import type { Metadata } from "next";
2-
import { Inter } from "next/font/google";
3-
import "./globals.css";
1+
import type { Metadata } from 'next';
2+
import { Inter, Fraunces, JetBrains_Mono } from 'next/font/google';
3+
import { Analytics } from '@vercel/analytics/next';
4+
import './globals.css';
45

5-
const inter = Inter({ subsets: ["latin"] });
6+
const inter = Inter({
7+
subsets: ['latin'],
8+
variable: '--font-sans',
9+
});
10+
11+
const fraunces = Fraunces({
12+
subsets: ['latin'],
13+
variable: '--font-serif',
14+
axes: ['opsz', 'SOFT'],
15+
});
16+
17+
const jetbrains = JetBrains_Mono({
18+
subsets: ['latin'],
19+
variable: '--font-mono',
20+
});
621

722
export const metadata: Metadata = {
8-
title: "Create Next App",
9-
description: "Generated by create next app",
23+
title: 'YouTube Caption Extractor',
24+
description:
25+
'Paste a YouTube URL — get the full transcript with timestamps.',
1026
};
1127

1228
export default function RootLayout({
1329
children,
14-
}: Readonly<{
15-
children: React.ReactNode;
16-
}>) {
30+
}: Readonly<{ children: React.ReactNode }>) {
1731
return (
18-
<html lang="en">
19-
<body className={inter.className}>{children}</body>
32+
<html
33+
lang='en'
34+
className={`${inter.variable} ${fraunces.variable} ${jetbrains.variable}`}
35+
>
36+
{/* suppressHydrationWarning silences benign mismatches caused by browser
37+
extensions (Grammarly, 1Password, etc.) that mutate <body> attributes
38+
after the server-rendered HTML has been sent. */}
39+
<body className='font-sans antialiased' suppressHydrationWarning>
40+
{children}
41+
<Analytics />
42+
</body>
2043
</html>
2144
);
2245
}

0 commit comments

Comments
 (0)