100 Commits
Author SHA1 Message Date
Ilia Mashkov fcc90e7a87 refactor(ImageLightbox): pin close button via a fixed wrapper 2026-07-14 18:02:37 +03:00
Ilia Mashkov 611a9c1a5a refactor(Button): distinct solid/outline variants, drop redundant secondary 2026-07-14 18:02:21 +03:00
Ilia Mashkov d479f3aabf fix(theme): render btn-shadow via box-shadow so light fills aren't tinted 2026-07-14 18:02:10 +03:00
Ilia Mashkov f1d488ae4f feat(Footer): icon-only CV download below sm 2026-07-14 17:33:55 +03:00
Ilia Mashkov 3131f846b7 fix(theme): shrink mobile footer to match its compact icon row 2026-07-14 17:33:44 +03:00
Ilia Mashkov 702be45ce3 refactor: rename section item to SectionPanel, list to SectionAccordion 2026-07-14 17:05:31 +03:00
Ilia Mashkov c3e8e38a58 fix(theme): fluid nav-title size and section rhythm off clamp tokens 2026-07-14 16:59:48 +03:00
Ilia Mashkov c0aab42e6c refactor(Card): drive sidebar border off lg: utilities not a media query 2026-07-14 16:19:22 +03:00
Ilia Mashkov e09fc467ba docs(theme): trim verbose CSS comments to their point 2026-07-14 15:59:48 +03:00
Ilia Mashkov d95840e127 fix(theme): give footer its own view-transition group
Without a named group the footer folds into the root snapshot, so every
section-body transition paints the section cards over it until the
transition ends.
2026-07-14 15:59:32 +03:00
Ilia Mashkov 21c771cf24 fix(theme): center list bullets on text optical centre 2026-07-14 15:59:19 +03:00
Ilia Mashkov 0353315953 fix(theme): clamp h3 so it never overtakes the nav h1 2026-07-14 15:59:05 +03:00
Ilia Mashkov b63e34413d fix(theme): clip overflow-x at root, not body
A scroll-container body makes Firefox clip descendant composited
transforms that briefly overshoot the viewport.
2026-07-14 15:58:51 +03:00
Ilia Mashkov e66114ce23 chore: pin turbopack root 2026-07-14 10:36:14 +03:00
Ilia Mashkov 03c51e2e8a docs: rewrite README for real stack and rendering model 2026-07-14 10:36:11 +03:00
Ilia Mashkov f8d13193ea refactor(Badge): forward native span props via spread 2026-07-14 10:36:08 +03:00
Ilia Mashkov fd15374134 feat(Link): tertiary variant with reversed hover 2026-07-14 10:35:57 +03:00
Ilia Mashkov 7c6da0ca73 feat(Stagger): staggered slide-in for list items 2026-07-14 10:35:49 +03:00
Ilia Mashkov 59304f4ae3 feat(theme): golden-ratio width cap for tag groups 2026-07-14 10:35:35 +03:00
Ilia Mashkov 1bd76d7061 fix(theme): squircle list markers, keep CMS li text inline 2026-07-14 10:35:23 +03:00
Ilia Mashkov 44abc362bc refactor(theme): muted offset shadows over hard brutalist stamp 2026-07-14 10:34:24 +03:00
Ilia Mashkov 48bd15533c fix: drop page grain behind content so cards and badges cover it 2026-07-13 17:55:36 +03:00
Ilia Mashkov 615f4afc2d fix: ul styles for rich text 2026-05-26 15:36:29 +03:00
Ilia Mashkov e16b88ba7e chore: remove outdated code 2026-05-23 13:14:06 +03:00
Ilia Mashkov 83ddd2724f chore: enforce common prop typing style 2026-05-23 13:06:56 +03:00
Ilia Mashkov 7e87cbc3ae chore: experience card responsive style tweak 2026-05-23 13:00:56 +03:00
Ilia Mashkov 521aa7d05c feat: create new grain-pattern utility and use it for body 2026-05-23 12:53:37 +03:00
Ilia Mashkov 532f93d896 fix: disable lightbox animation in firefox since it isnt supported yet 2026-05-23 12:52:16 +03:00
Ilia Mashkov 9ebb515032 feat(projects): prioritize LCP image, fix View Project button on mobile
- ProjectCard accepts a `priority` prop forwarded to the thumbnail Image so
  above-the-fold cards skip lazy-loading and get a preload hint.
- ProjectsSection marks the first card *with an image* as priority; handles
  the case where the first project has no image and the LCP candidate ends
  up being a later card.
- View Project button drops `w-full` on mobile (collapsed sidebar above the
  card body), using `self-start` + `text-center` instead so it sizes to its
  content. Restores column-filling on lg+ where the sidebar is its own
  narrow column.
2026-05-23 09:54:43 +03:00
Ilia Mashkov cd59766f92 feat(ImageLightbox): morph to dialog via View Transitions, fix sizing and stacking
- Use the shared Modal for dialog mechanics; ImageLightbox keeps only the
  view-transition coordination (just-in-time view-transition-name handoff
  between thumb and dialog frame, ESC routed through onCancel).
- Switch to <Image> for both thumb and dialog image; thumb is priority/sizes-
  driven for LCP, dialog image is lazy-loaded (deferred until open).
- New brutal-outline utility for the dialog frame: outline paints after
  children so subpixel image bleed can't cover it; dialog gets overflow-
  visible so the outline isn't clipped.
- lightbox-image utility caps image dimensions to viewport minus close-button
  and footer headroom, with box-sizing: content-box.
- Lightbox dialog gets view-transition-name lightbox-dialog (z=20) and the
  frame gets lightbox-frame (z=30) so both stack cleanly above the page
  during the open/close transition. Footer drops its named VT group since
  section-body no longer slides over it.
- Cream-tinted backdrop replaces the blur (Firefox-friendly), color-mix
  with var(--cream) for the token reference.
- scrollbar-gutter: stable on html so locking body scroll doesn't shift
  the layout.
2026-05-23 09:54:20 +03:00
Ilia Mashkov ecbb76312b refactor(section): snap-out old section-body on navigation
The explicit fade-and-slide-left OLD animation left a visible ghost of the
previous section's content behind the incoming slide. Replacing it with an
instant opacity:0 keeps the transition clean while preserving the NEW
slide-in delay so the snap-out has a beat to register. Drops the now-dead
keyframes and --slide-section-body-out token.
2026-05-23 09:53:26 +03:00
Ilia Mashkov 82933dedf8 feat(shared): add Modal component
Native <dialog>+showModal() wrapper with imperative open()/close() via ref,
body scroll lock, and backdrop-click/keyboard-close behaviors. Exposes
onCancel and onBackdropClose escape hatches for consumers that need to
wrap the close path (e.g. in a view transition).
2026-05-23 09:50:42 +03:00
Ilia Mashkov c4002ebb4f chore: use node:path protocol in vitest config 2026-05-23 09:50:29 +03:00
Ilia Mashkov a31cf4deec fix: opt Next.js out of smooth scroll for route transitions
Adds data-scroll-behavior="smooth" to <html> so Next disables the global
scroll-behavior during programmatic route-change scrolls while keeping
smooth behavior for user-driven anchor jumps. Silences the Next 15 warning.
2026-05-22 15:30:42 +03:00
Ilia Mashkov 5b686ad87c fix: SectionAccordion animation misbehave 2026-05-22 14:22:08 +03:00
Ilia Mashkov 43242c3bed refactor: swap Button ghost/outline semantics, clean up ImageLightbox thumbnail
ghost now means transparent bg (no fill); outline keeps cream bg with subtle border.
Remove magnify icon overlay from ImageLightbox thumbnail — hover cursor-zoom-in is sufficient.
Close button updated to variant="outline" for cream-on-blue contrast in the dialog.
2026-05-22 14:22:08 +03:00
Ilia Mashkov 7a06d42d20 feat: add icon components and update ImageLightbox with icons and Button 2026-05-22 12:48:54 +03:00
Ilia Mashkov eeb7d6b4a6 feat: use ImageLightbox in ProjectCard 2026-05-22 12:18:00 +03:00
Ilia Mashkov eb13328f9a feat: add lightbox backdrop CSS and export ImageLightbox 2026-05-22 12:05:42 +03:00
Ilia Mashkov bfb0b46a37 feat: implement ImageLightbox with tests 2026-05-22 12:04:11 +03:00
Ilia Mashkov c7ed458c8e test: ImageLightbox failing tests 2026-05-22 10:14:53 +03:00
Ilia Mashkov 49cafe7161 feat: ImageLightbox placeholder 2026-05-21 20:28:28 +03:00
Ilia Mashkov 1b0ffd41a2 chore: changes for deployment to get the pocketbase public url variable value 2026-05-21 18:15:16 +03:00
Ilia Mashkov 3e520f6abb fix: use https protocol in next config 2026-05-21 18:01:03 +03:00
Ilia Mashkov 4d54947a91 fix: tweak slide section animation 2026-05-21 18:00:12 +03:00
Ilia Mashkov f121443e52 fix: add footer z index in transition group to stay above main content during transitions 2026-05-21 17:59:40 +03:00
Ilia Mashkov df4526cabd chore: remove unused favicon.ico 2026-05-21 17:07:58 +03:00
Ilia Mashkov bf36a40bb5 chore: swap the favicon 2026-05-21 16:57:30 +03:00
Ilia Mashkov 886cf4b5c4 feat: add spring slide animation for section content 2026-05-21 16:36:00 +03:00
Ilia Mashkov fc588f9e66 feat: set page title and description 2026-05-21 16:01:42 +03:00
Ilia Mashkov f08ee51332 chore: remove unused files 2026-05-21 15:58:48 +03:00
Ilia Mashkov 9ded41db3c feat: set the favicon 2026-05-21 15:58:11 +03:00
Ilia Mashkov 0697e9ad72 feat: add error handling and tests for client.ts 2026-05-21 15:57:43 +03:00
Ilia Mashkov caff3fe7e3 fix: align footer paddings with main ones 2026-05-21 15:55:47 +03:00
Ilia Mashkov 9deefaf3fc feat: add PBHttpError and try/catch in getFirstRecord 2026-05-21 13:05:31 +03:00
Ilia MashkovandClaude Sonnet 4.6 6b15a0e658 fix: gracefully handle PocketBase unreachable during static generation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 19:27:04 +03:00
Ilia MashkovandClaude Sonnet 4.6 93b8adf55d fix: require PB_URL in production, fall back to localhost in dev only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 19:26:58 +03:00
Ilia Mashkov 06d69a860e fix: add css and json to biome check, set yarn version in package.json 2026-05-19 18:57:16 +03:00
Ilia Mashkov 181cfdebdf fix: old palette purged from stories and stories purged from production build 2026-05-19 18:50:45 +03:00
Ilia Mashkov e0565d6ddc fix: yarn instead of npm for dockerfile
Build and push / build (push) Failing after 2m17s
2026-05-19 18:37:58 +03:00
Ilia MashkovandClaude Sonnet 4.6 dd9cc766d5 feat: add route-level error page and per-section error boundary
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 18:20:53 +03:00
Ilia Mashkov d62c0ad501 chore: add Gitea Actions deploy workflow 2026-05-19 18:09:59 +03:00
Ilia Mashkov cf2f1bc7f3 chore: unignore .gitea directory 2026-05-19 18:09:26 +03:00
Ilia Mashkov 7f6e6369ff fix: reduce padding and spacing for mobile
Main: px-4 py-6 on mobile (was px-8 py-12). Section accordion:
mb/py on inactive links tightened to 1/1 on mobile, space-y-0
between sections. Active title text-xl on mobile to prevent
wrapping at ~400px, matches inactive title size.
2026-05-19 18:06:51 +03:00
Ilia Mashkov d0f09f0dbd feat: social links with inline SVG icons from CMS
SocialRecord gains icon field (SVG markup string). InlineSvg component
parses SVG string via html-react-parser. Footer renders icon on mobile
(sm:hidden label), label on sm+ (hidden icon). Email field refactored
from string to SocialRecord relation.
2026-05-19 18:06:20 +03:00
Ilia Mashkov 41af0b90a0 feat: add Link secondary variant with border-bottom at sm+
Secondary variant drops text-decoration, uses opacity-60/hover-100
and brutal-border-bottom at sm+ for use in icon+label links where
the underline should only appear alongside the label.
2026-05-19 18:06:10 +03:00
Ilia Mashkov 954b17d824 fix: reduce Button sm padding on mobile 2026-05-19 18:05:57 +03:00
Ilia Mashkov 906ec3b805 feat: fixed footer with responsive height tokens
Footer is fixed bottom-0 with h-footer (5rem mobile) / md:h-footer-wide
(4rem desktop). Body gets matching pb-footer/md:pb-footer-wide to
reserve space. Tokens registered in @theme inline as --spacing-footer*.
2026-05-19 18:05:46 +03:00
Ilia Mashkov 4d6d78a528 chore: add .dockerignore 2026-05-19 18:05:37 +03:00
Ilia Mashkov f40e9f54a3 chore: add dockerfile 2026-05-19 09:55:12 +03:00
Ilia Mashkov 7829f81d1a chore: add dockerfile 2026-05-19 09:46:35 +03:00
Ilia Mashkov d5ba77b4ce feat: add poweredByHeader: false 2026-05-18 21:40:10 +03:00
Ilia Mashkov 5c00f8e8a0 feat: add /api/revalidate webhook for on-demand ISR
POST with x-revalidate-secret header and { tag } body calls
revalidateTag to purge a collection from the Next.js data cache.
Guarded by REVALIDATE_SECRET env var.
2026-05-18 21:34:51 +03:00
Ilia Mashkov cb3bdce24a feat: tag all PocketBase fetches for ISR cache invalidation
Each getCollection/getFirstRecord call now passes the collection name
as a cache tag so revalidateTag can target individual collections.
2026-05-18 21:34:43 +03:00
Ilia Mashkov 42ca683c65 feat: add tags/revalidate options to PocketBase fetch client
PB_URL falls back through PB_URL → NEXT_PUBLIC_PB_URL → localhost so
internal Docker hostname is used server-side without leaking into the
client bundle. cache: force-cache replaced with next: { tags, revalidate }
for ISR tag-based invalidation.
2026-05-18 21:34:34 +03:00
Ilia Mashkov fea6682024 feat: switch to standalone output with PocketBase remotePatterns
Drops static export (STATIC_EXPORT env var) in favour of standalone
for ISR. Images remotePatterns reads PB_HOSTNAME/PB_PORT env vars so
Docker internal hostname works without hardcoding.
2026-05-18 21:34:25 +03:00
Ilia Mashkov 540df57f8d feat: add 404 page with centered layout
not-found.tsx renders oversized Fraunces heading with a back link.
Body gets flex flex-col min-h-screen so main can flex-1 to fill
available height without pushing the footer off screen.
2026-05-18 20:46:22 +03:00
Ilia Mashkov b88263a65a fix: DetailedProjectCard — render description as RichText 2026-05-18 20:46:13 +03:00
Ilia Mashkov 06e39b58c6 refactor: ProjectsSection — use shared buildFileUrl, pass url prop, switch to stacked layout 2026-05-18 20:46:02 +03:00
Ilia Mashkov ac9ee0eb4e feat: ProjectCard — add url prop, RichText description, open link in new tab 2026-05-18 20:45:54 +03:00
Ilia Mashkov 2ae5ae3210 feat: wire Footer to PocketBase site_settings
Fetches CV file, email and social links via expand=contacts,contacts.socials.
CV rendered as polymorphic Button with download attr; socials and email
rendered as Link components.
2026-05-18 20:45:44 +03:00
Ilia Mashkov f159c6e861 feat: add SocialRecord, ContactsRecord, SiteSettingsRecord API types
Models PocketBase relations: SiteSettings → contacts → ContactsRecord
→ socials[] → SocialRecord. expand fields typed as optional resolved
records for use with PocketBase expand query param.
2026-05-18 20:45:32 +03:00
Ilia Mashkov b33b9f328c feat: add Link shared component
Renders Next.js Link for internal routes, plain anchor with
target="_blank" rel="noopener noreferrer" when external prop is set.
2026-05-18 20:45:17 +03:00
Ilia Mashkov c9631f9905 feat: add buildFileUrl utility with tests
Moved from ProjectsSection inline function to shared/lib/utils.
Accepts optional baseUrl for testability without env mocking.
2026-05-18 20:45:06 +03:00
Ilia Mashkov ba7395cb32 feat: make Button polymorphic — renders <a> when href is provided
Discriminated union types (AsButton | AsAnchor), isAnchorProps type guard
eliminates all 'as' casts. as const satisfies for VARIANTS/SIZES lookup
tables. brutal-border replaces border-[3px] in ghost variant.
2026-05-18 20:44:50 +03:00
Ilia Mashkov 7e542597d0 feat: Footer widget with email link and CV download, added to root layout 2026-05-18 14:15:25 +03:00
Ilia Mashkov 0552a2a8e5 refactor: register text-section-title in @theme inline, use as plain utility class 2026-05-18 14:06:01 +03:00
Ilia Mashkov d955aeb628 refactor: replace inline style with Tailwind class and font-wonk utility 2026-05-18 14:04:56 +03:00
Ilia Mashkov b40ff4f588 fix: fluid section title with clamp() to prevent wrapping below 900px 2026-05-18 14:02:03 +03:00
Ilia Mashkov 531de6899e refactor: ProjectCard sm button, left-border year matching ExperienceCard style 2026-05-18 13:20:47 +03:00
Ilia Mashkov 10034ec561 refactor: ProjectCard sidebar layout — year, tags, button in sidebar 2026-05-18 13:14:40 +03:00
Ilia Mashkov 458ee0e449 refactor: CardSidebar layout breakpoint md → lg for wider description area 2026-05-18 13:11:53 +03:00
Ilia Mashkov 979e2071d1 refactor: widen section and sidebar, plain period text, Badge xs size for stack 2026-05-18 13:07:01 +03:00
Ilia Mashkov 37098be3c8 feat: Badge size prop (sm/md) and use Badge in ExperienceCard 2026-05-18 13:02:07 +03:00
Ilia Mashkov 48a08ec3fb feat: formatMonthYearRange — period now includes abbreviated month 2026-05-18 13:01:58 +03:00
Ilia Mashkov 1550989fd9 feat: CardSidebar layout component and ExperienceCard sidebar redesign
Sidebar: period badge, company, stack tags.
Main: role title and rich-text description.
2026-05-18 12:51:33 +03:00
Ilia Mashkov 782c619a91 feat: ExperienceCard stack field and Card subcomponent layout 2026-05-18 12:39:41 +03:00
Ilia Mashkov 543020f85c feat: apply Fraunces font to ProjectCard title 2026-05-18 12:39:33 +03:00
Ilia Mashkov e00c1460e1 refactor: responsive spacing on CardHeader and CardFooter 2026-05-18 12:39:20 +03:00
Ilia Mashkov f874a943ff fix: a11y — accessible label on SectionAccordion, opacity-60 on category headings 2026-05-18 12:39:07 +03:00