Compare commits
5
Commits
747fa89322
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe129bc92e | ||
|
|
9e734f29b8 | ||
|
|
b8f7168d64 | ||
|
|
d8eb5d7504 | ||
|
|
61a87b1f9a |
@@ -0,0 +1,34 @@
|
|||||||
|
name: Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: deploy
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 12
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 26
|
||||||
|
cache: pnpm
|
||||||
|
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- run: pnpm build
|
||||||
|
|
||||||
|
- run: pnpm exec wrangler deploy
|
||||||
|
env:
|
||||||
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
@@ -39,3 +39,10 @@ yarn-error.log*
|
|||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
|
|
||||||
|
#Ignore vscode AI rules
|
||||||
|
.github/instructions/codacy.instructions.md
|
||||||
|
|
||||||
|
# wrangler
|
||||||
|
.wrangler
|
||||||
|
|||||||
@@ -1,36 +1,44 @@
|
|||||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
# Woiken Website
|
||||||
|
|
||||||
## Getting Started
|
The landing page for [woiken.com](https://woiken.com), a coming-soon page for Woiken.
|
||||||
|
|
||||||
First, run the development server:
|
It's a single page built with Next.js 16 and exported as static files. Scrolling opens a
|
||||||
|
framed sky photo up to full screen, where the "A new cloud, coming soon." tagline fades in.
|
||||||
|
|
||||||
```bash
|
## Commands
|
||||||
npm run dev
|
|
||||||
# or
|
| Command | Action |
|
||||||
yarn dev
|
| :------------------------- | :-------------------------------------------------- |
|
||||||
# or
|
| `pnpm install` | Install dependencies |
|
||||||
pnpm dev
|
| `pnpm dev` | Start the dev server at `localhost:3000` |
|
||||||
# or
|
| `pnpm build` | Build the static site to `./out/` |
|
||||||
bun dev
|
| `pnpm lint` | Lint with ESLint |
|
||||||
|
| `pnpm exec wrangler dev` | Serve `./out/` locally the way Cloudflare will |
|
||||||
|
|
||||||
|
Use pnpm 12. `pnpm start` doesn't work, because the site is a static export (`output: "export"`).
|
||||||
|
|
||||||
|
## Project structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── app/
|
||||||
|
│ ├── page.tsx # The landing page
|
||||||
|
│ ├── layout.tsx
|
||||||
|
│ └── globals.css # Tailwind + photo darkening
|
||||||
|
├── components/
|
||||||
|
│ └── ScrollExpand/ # Scroll-to-expand hero (from React Bits)
|
||||||
|
├── public/
|
||||||
|
│ └── grass-field.jpg # Hero photo
|
||||||
|
├── wrangler.jsonc # Cloudflare Worker config
|
||||||
|
└── .gitea/workflows/deploy.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
## Deployment
|
||||||
|
|
||||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
Every push to `main` deploys the site via `.gitea/workflows/deploy.yml`. The workflow builds
|
||||||
|
the site and runs `wrangler deploy`, which uploads `./out/` as static assets to the
|
||||||
|
`woiken-website` Worker on the `woiken.com` custom domain.
|
||||||
|
## Credits
|
||||||
|
|
||||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
Hero photo: [grass field at daytime](https://unsplash.com/photos/grass-field-at-daytime-PEm_sLmJT-w)
|
||||||
|
on Unsplash.
|
||||||
## Learn More
|
|
||||||
|
|
||||||
To learn more about Next.js, take a look at the following resources:
|
|
||||||
|
|
||||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
||||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
||||||
|
|
||||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
||||||
|
|
||||||
## Deploy on Vercel
|
|
||||||
|
|
||||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
||||||
|
|
||||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
|
||||||
|
|||||||
@@ -2,3 +2,8 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
|
||||||
|
/* Tone down the bright sky so the white type reads on top of it */
|
||||||
|
.woiken-hero .scroll-expand__media {
|
||||||
|
filter: brightness(0.72) saturate(1.1);
|
||||||
|
}
|
||||||
|
|||||||
+23
-18
@@ -1,26 +1,31 @@
|
|||||||
"use client";
|
import ScrollExpand from "@/components/ScrollExpand/ScrollExpand";
|
||||||
|
|
||||||
import { motion } from "framer-motion";
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[#0b0b08] text-white">
|
<div className="min-h-screen bg-[#0b0b08] text-white">
|
||||||
<header className="mx-auto flex w-full max-w-6xl items-center justify-center px-6 py-6 md:px-10">
|
<ScrollExpand
|
||||||
<div className="text-lg font-semibold tracking-wide">Woiken</div>
|
className="woiken-hero"
|
||||||
</header>
|
src="/grass-field.jpg"
|
||||||
|
alt="Clouds drifting over a green field under a blue sky"
|
||||||
<motion.main
|
title="Woiken"
|
||||||
initial={{ opacity: 0, y: 18 }}
|
scrollHint="Scroll"
|
||||||
animate={{ opacity: 1, y: 0 }}
|
startWidth={46}
|
||||||
transition={{ duration: 0.55, ease: "easeOut" }}
|
startHeight={56}
|
||||||
className="mx-auto flex min-h-[calc(100vh-84px)] w-full max-w-6xl flex-col items-center justify-center px-6 pb-20 text-center md:px-10"
|
overlayScrim={0.55}
|
||||||
|
useWindowScroll
|
||||||
>
|
>
|
||||||
<section className="max-w-3xl">
|
<p className="mb-4 text-xs font-medium uppercase tracking-[0.3em] text-white/70 md:text-sm">
|
||||||
<h1 className="text-4xl font-medium leading-tight tracking-tight md:text-6xl">
|
Woiken
|
||||||
Woiken is coming soon.
|
</p>
|
||||||
</h1>
|
<h1 className="text-5xl font-semibold leading-[1.05] tracking-tight md:text-7xl lg:text-8xl">
|
||||||
</section>
|
A new cloud
|
||||||
</motion.main>
|
</h1>
|
||||||
|
<p className="mt-5 text-lg text-white/80 md:text-2xl">coming soon.</p>
|
||||||
|
</ScrollExpand>
|
||||||
|
|
||||||
|
<footer className="px-6 py-10 text-center text-sm text-white/40">
|
||||||
|
© {new Date().getFullYear()} Woiken
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
.scroll-expand {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-expand--scroller {
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-expand--scroller::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-expand__track {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-expand__stage {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
--se-title-size: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-expand__frame {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
clip-path: inset(21% 29% 21% 29% round 24px);
|
||||||
|
will-change: clip-path;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-expand__media {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
will-change: transform;
|
||||||
|
transform-origin: center;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-expand__scrim {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
background: linear-gradient(to top, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.1) 45%, rgba(0, 0, 0, 0.35));
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-expand__overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
padding: 6%;
|
||||||
|
opacity: 0;
|
||||||
|
will-change: opacity, transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-expand__title {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 6%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: var(--se-title-size);
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
line-height: 1;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 2px 24px rgba(0, 0, 0, 0.45);
|
||||||
|
pointer-events: none;
|
||||||
|
will-change: opacity, transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-expand__hint {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 1.25rem;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
color: rgba(255, 255, 255, 0.55);
|
||||||
|
pointer-events: none;
|
||||||
|
will-change: opacity, transform;
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
import type { CSSProperties, HTMLAttributes, ReactNode } from "react";
|
||||||
|
|
||||||
|
export interface ScrollExpandProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
||||||
|
src?: string;
|
||||||
|
mediaType?: "image" | "video";
|
||||||
|
poster?: string;
|
||||||
|
alt?: string;
|
||||||
|
title?: string;
|
||||||
|
scrollHint?: string;
|
||||||
|
startWidth?: number;
|
||||||
|
startHeight?: number;
|
||||||
|
startRadius?: number;
|
||||||
|
endRadius?: number;
|
||||||
|
mediaZoom?: number;
|
||||||
|
scrollDistance?: number;
|
||||||
|
holdDistance?: number;
|
||||||
|
smoothing?: number;
|
||||||
|
overlayScrim?: number;
|
||||||
|
useWindowScroll?: boolean;
|
||||||
|
enabled?: boolean;
|
||||||
|
children?: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
style?: CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const ScrollExpand: (props: ScrollExpandProps) => JSX.Element;
|
||||||
|
|
||||||
|
export default ScrollExpand;
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
import './ScrollExpand.css';
|
||||||
|
|
||||||
|
const clamp = (v, a, b) => (v < a ? a : v > b ? b : v);
|
||||||
|
|
||||||
|
const smoothstep = (edge0, edge1, x) => {
|
||||||
|
const t = clamp((x - edge0) / (edge1 - edge0 || 1e-6), 0, 1);
|
||||||
|
return t * t * (3 - 2 * t);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ScrollExpand = ({
|
||||||
|
src = '',
|
||||||
|
mediaType = 'image',
|
||||||
|
poster = '',
|
||||||
|
alt = '',
|
||||||
|
title = '',
|
||||||
|
scrollHint = '',
|
||||||
|
startWidth = 42,
|
||||||
|
startHeight = 58,
|
||||||
|
startRadius = 24,
|
||||||
|
endRadius = 0,
|
||||||
|
mediaZoom = 1.35,
|
||||||
|
scrollDistance = 1.2,
|
||||||
|
holdDistance = 0.35,
|
||||||
|
smoothing = 0.1,
|
||||||
|
overlayScrim = 0.45,
|
||||||
|
useWindowScroll = false,
|
||||||
|
enabled = true,
|
||||||
|
children,
|
||||||
|
className = '',
|
||||||
|
style,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
const rootRef = useRef(null);
|
||||||
|
const trackRef = useRef(null);
|
||||||
|
const stageRef = useRef(null);
|
||||||
|
const frameRef = useRef(null);
|
||||||
|
const mediaRef = useRef(null);
|
||||||
|
const titleRef = useRef(null);
|
||||||
|
const overlayRef = useRef(null);
|
||||||
|
const scrimRef = useRef(null);
|
||||||
|
const hintRef = useRef(null);
|
||||||
|
|
||||||
|
const propsRef = useRef({});
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
propsRef.current = {
|
||||||
|
startWidth,
|
||||||
|
startHeight,
|
||||||
|
startRadius,
|
||||||
|
endRadius,
|
||||||
|
mediaZoom,
|
||||||
|
scrollDistance,
|
||||||
|
holdDistance,
|
||||||
|
smoothing,
|
||||||
|
overlayScrim,
|
||||||
|
useWindowScroll,
|
||||||
|
enabled
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const applyProgress = useCallback(p => {
|
||||||
|
const frame = frameRef.current;
|
||||||
|
const media = mediaRef.current;
|
||||||
|
if (!frame || !media) return;
|
||||||
|
const c = propsRef.current;
|
||||||
|
|
||||||
|
const e = smoothstep(0, 1, p);
|
||||||
|
|
||||||
|
const w = c.startWidth + (100 - c.startWidth) * e;
|
||||||
|
const h = c.startHeight + (100 - c.startHeight) * e;
|
||||||
|
const ix = Math.max(0, (100 - w) / 2);
|
||||||
|
const iy = Math.max(0, (100 - h) / 2);
|
||||||
|
const r = c.startRadius + (c.endRadius - c.startRadius) * e;
|
||||||
|
frame.style.clipPath = `inset(${iy}% ${ix}% ${iy}% ${ix}% round ${r}px)`;
|
||||||
|
|
||||||
|
media.style.transform = `scale(${c.mediaZoom + (1 - c.mediaZoom) * e})`;
|
||||||
|
|
||||||
|
if (scrimRef.current) scrimRef.current.style.opacity = `${c.overlayScrim * e}`;
|
||||||
|
|
||||||
|
if (titleRef.current) {
|
||||||
|
const out = smoothstep(0.4, 0.88, p);
|
||||||
|
titleRef.current.style.opacity = `${1 - out}`;
|
||||||
|
titleRef.current.style.transform = `translate3d(0, ${-28 * out}px, 0) scale(${1 + 0.06 * out})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hintRef.current) {
|
||||||
|
const gone = smoothstep(0, 0.12, p);
|
||||||
|
hintRef.current.style.opacity = `${1 - gone}`;
|
||||||
|
hintRef.current.style.transform = `translate3d(0, ${8 * gone}px, 0)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overlayRef.current) {
|
||||||
|
const inn = smoothstep(0.68, 1, p);
|
||||||
|
overlayRef.current.style.opacity = `${inn}`;
|
||||||
|
overlayRef.current.style.transform = `translate3d(0, ${18 * (1 - inn)}px, 0)`;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const root = rootRef.current;
|
||||||
|
const track = trackRef.current;
|
||||||
|
const stage = stageRef.current;
|
||||||
|
if (!root || !track || !stage) return;
|
||||||
|
|
||||||
|
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
|
||||||
|
let raf = 0;
|
||||||
|
let current = 0;
|
||||||
|
let target = 0;
|
||||||
|
let stageH = 0;
|
||||||
|
let running = false;
|
||||||
|
|
||||||
|
const measure = () => {
|
||||||
|
const c = propsRef.current;
|
||||||
|
stageH = c.useWindowScroll ? window.innerHeight : root.clientHeight;
|
||||||
|
if (stageH <= 0) return;
|
||||||
|
stage.style.height = `${stageH}px`;
|
||||||
|
track.style.height = `${stageH * (1 + Math.max(0, c.scrollDistance) + Math.max(0, c.holdDistance))}px`;
|
||||||
|
|
||||||
|
const w = root.clientWidth || stageH;
|
||||||
|
stage.style.setProperty('--se-title-size', `${clamp(w * 0.075, 20, 84)}px`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const readProgress = () => {
|
||||||
|
const c = propsRef.current;
|
||||||
|
if (!c.enabled) return 1;
|
||||||
|
const span = stageH * Math.max(0.01, c.scrollDistance);
|
||||||
|
if (c.useWindowScroll) {
|
||||||
|
const top = track.getBoundingClientRect().top;
|
||||||
|
return clamp(-top / span, 0, 1);
|
||||||
|
}
|
||||||
|
return clamp(root.scrollTop / span, 0, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tick = () => {
|
||||||
|
const c = propsRef.current;
|
||||||
|
const k = c.smoothing <= 0 ? 1 : 1 - Math.exp(-1 / (60 * c.smoothing));
|
||||||
|
current += (target - current) * k;
|
||||||
|
if (Math.abs(target - current) < 0.0004) {
|
||||||
|
current = target;
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
applyProgress(current);
|
||||||
|
raf = running ? requestAnimationFrame(tick) : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const kick = () => {
|
||||||
|
if (running) return;
|
||||||
|
running = true;
|
||||||
|
if (!raf) raf = requestAnimationFrame(tick);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onScroll = () => {
|
||||||
|
target = readProgress();
|
||||||
|
if (propsRef.current.smoothing <= 0 || reduceMotion) {
|
||||||
|
current = target;
|
||||||
|
applyProgress(current);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kick();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onResize = () => {
|
||||||
|
measure();
|
||||||
|
target = readProgress();
|
||||||
|
current = target;
|
||||||
|
applyProgress(current);
|
||||||
|
};
|
||||||
|
|
||||||
|
measure();
|
||||||
|
target = readProgress();
|
||||||
|
current = target;
|
||||||
|
applyProgress(current);
|
||||||
|
|
||||||
|
const scroller = useWindowScroll ? window : root;
|
||||||
|
scroller.addEventListener('scroll', onScroll, { passive: true });
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
const ro = new ResizeObserver(onResize);
|
||||||
|
ro.observe(root);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (raf) cancelAnimationFrame(raf);
|
||||||
|
scroller.removeEventListener('scroll', onScroll);
|
||||||
|
window.removeEventListener('resize', onResize);
|
||||||
|
ro.disconnect();
|
||||||
|
};
|
||||||
|
}, [applyProgress, useWindowScroll]);
|
||||||
|
|
||||||
|
const media =
|
||||||
|
mediaType === 'video' ? (
|
||||||
|
<video
|
||||||
|
ref={mediaRef}
|
||||||
|
className="scroll-expand__media"
|
||||||
|
src={src}
|
||||||
|
poster={poster}
|
||||||
|
autoPlay
|
||||||
|
muted
|
||||||
|
loop
|
||||||
|
playsInline
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
|
<img ref={mediaRef} className="scroll-expand__media" src={src} alt={alt} draggable={false} />
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={rootRef}
|
||||||
|
className={`scroll-expand ${useWindowScroll ? '' : 'scroll-expand--scroller'} ${className}`.trim()}
|
||||||
|
style={style}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
<div ref={trackRef} className="scroll-expand__track">
|
||||||
|
<div ref={stageRef} className="scroll-expand__stage">
|
||||||
|
<div ref={frameRef} className="scroll-expand__frame">
|
||||||
|
{media}
|
||||||
|
<div ref={scrimRef} className="scroll-expand__scrim" />
|
||||||
|
{children ? (
|
||||||
|
<div ref={overlayRef} className="scroll-expand__overlay">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
{title ? (
|
||||||
|
<div ref={titleRef} className="scroll-expand__title">
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{scrollHint ? (
|
||||||
|
<div ref={hintRef} className="scroll-expand__hint">
|
||||||
|
{scrollHint}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScrollExpand;
|
||||||
+8
-13
@@ -1,16 +1,11 @@
|
|||||||
import { dirname } from "path";
|
import { defineConfig, globalIgnores } from "eslint/config";
|
||||||
import { fileURLToPath } from "url";
|
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||||
import { FlatCompat } from "@eslint/eslintrc";
|
import nextTs from "eslint-config-next/typescript";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const eslintConfig = defineConfig([
|
||||||
const __dirname = dirname(__filename);
|
...nextVitals,
|
||||||
|
...nextTs,
|
||||||
const compat = new FlatCompat({
|
globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts", "tailwind.config.ts"]),
|
||||||
baseDirectory: __dirname,
|
]);
|
||||||
});
|
|
||||||
|
|
||||||
const eslintConfig = [
|
|
||||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
|
||||||
];
|
|
||||||
|
|
||||||
export default eslintConfig;
|
export default eslintConfig;
|
||||||
|
|||||||
+12
-13
@@ -3,33 +3,32 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.475.0",
|
"lucide-react": "^0.475.0",
|
||||||
"motion": "^12.4.7",
|
"motion": "^12.4.7",
|
||||||
"next": "15.1.7",
|
"next": "16.3.6",
|
||||||
"next-themes": "^0.4.4",
|
"next-themes": "^0.4.4",
|
||||||
"react": "^19.0.0",
|
"react": "^19.3.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.3.0",
|
||||||
"tailwind-merge": "^3.0.2",
|
"tailwind-merge": "^3.0.2",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19.3.0",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19.3.0",
|
||||||
"eslint": "^9",
|
"eslint": "^9.39.5",
|
||||||
"eslint-config-next": "15.1.7",
|
"eslint-config-next": "16.3.6",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5"
|
"typescript": "^5",
|
||||||
},
|
"wrangler": "^4.142.0"
|
||||||
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+5404
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
|||||||
|
allowBuilds:
|
||||||
|
esbuild: true
|
||||||
|
sharp: false
|
||||||
|
workerd: true
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.5 MiB |
+19
-5
@@ -1,7 +1,11 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2017",
|
"target": "ES2017",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
@@ -11,7 +15,7 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "react-jsx",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
@@ -19,9 +23,19 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": [
|
||||||
|
"./*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": [
|
||||||
"exclude": ["node_modules"]
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
".next/dev/types/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"$schema": "node_modules/wrangler/config-schema.json",
|
||||||
|
"name": "woiken-website",
|
||||||
|
"compatibility_date": "2026-09-28",
|
||||||
|
"assets": {
|
||||||
|
"directory": "./out",
|
||||||
|
"not_found_handling": "404-page"
|
||||||
|
},
|
||||||
|
"routes": [{ "pattern": "woiken.com", "custom_domain": true }]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user