import Header from '@/components/Header'; export default function Layout({ children }) { return ( <main className="app-shell"> <Header /> {children} </main> ); }
const useTheme = () => { const [theme, setTheme] = useState('dark'); return { theme, setTheme }; };
@media (min-width: 1024px) { .grid { display: grid; grid-template-columns: 320px 1fr; gap: 2rem; } }
import Image from 'next/image'; <Image src={work.image} alt={work.title} width={400} height={300} />
export const NAV_ITEMS = [ { id: 'home', href: '/' }, { id: 'works', href: '/works' }, { id: 'contact', href: '/contact' }, ];
useEffect(() => { if (!isOpen) return; document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = ''; }; }, [isOpen]);
.portfolio-modal__panel {
display: flex;
flex-direction: column;
max-height: calc(100dvh - 48px);
border-radius: 16px;
animation: portfolio-modal-panel-in 0.4s ease;
}const WorksSection = () => { const [activeWork, setActiveWork] = useState(null); return ( <ul className="portfolio-grid"> {portfolioWorks.map(...)} </ul> ); };
/** Site shell — shared layout */ export default function SiteLayout({ children }) { return ( <ContentScroll>{children}</ContentScroll> ); }
if (e.key === 'Escape') { requestClose(); } @media (prefers-reduced-motion: reduce) { animation: none; }
import Header from '@/components/Header'; export default function Layout({ children }) { return ( <main className="app-shell"> <Header /> {children} </main> ); }
const useTheme = () => { const [theme, setTheme] = useState('dark'); return { theme, setTheme }; };
@media (min-width: 1024px) { .grid { display: grid; grid-template-columns: 320px 1fr; gap: 2rem; } }
import Image from 'next/image'; <Image src={work.image} alt={work.title} width={400} height={300} />
export const NAV_ITEMS = [ { id: 'home', href: '/' }, { id: 'works', href: '/works' }, { id: 'contact', href: '/contact' }, ];
useEffect(() => { if (!isOpen) return; document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = ''; }; }, [isOpen]);
.portfolio-modal__panel {
display: flex;
flex-direction: column;
max-height: calc(100dvh - 48px);
border-radius: 16px;
animation: portfolio-modal-panel-in 0.4s ease;
}const WorksSection = () => { const [activeWork, setActiveWork] = useState(null); return ( <ul className="portfolio-grid"> {portfolioWorks.map(...)} </ul> ); };
/** Site shell — shared layout */ export default function SiteLayout({ children }) { return ( <ContentScroll>{children}</ContentScroll> ); }
if (e.key === 'Escape') { requestClose(); } @media (prefers-reduced-motion: reduce) { animation: none; }
async function fetchWorks() { const res = await fetch('/api/works'); return res.json(); }
<SimpleBar> <ContentSide /> </SimpleBar> // client navigation, no reload
type Skill = { id: string; title: string; level: number; };
import Link from 'next/link'; import { usePathname } from 'next/navigation'; const pathname = usePathname(); // /works, /skills, /contact
export const metadata = { title: 'Портфолио | SasWeb', description: 'Примеры работ frontend-разработки', };
.center-information__scroll { .simplebar-scrollbar ::before { background: linear-gradient( 180deg, var(--accent-start), var(--accent-end) ); } }
const { isClosing, requestClose } = useModalClose(onClose); createPortal( <div className="portfolio-modal"> <ContactForm /> </div>, document.body );
const handleSubmit = (e) => { e.preventDefault(); const { name, email, message } = form; // send to API / mail service };
:root { --accent-start: #00c897; --accent-end: #007bfd; --page-bg: #050a0e; } html.theme-light { --page-bg: #eef4f6; }
async function fetchWorks() { const res = await fetch('/api/works'); return res.json(); }
<SimpleBar> <ContentSide /> </SimpleBar> // client navigation, no reload
type Skill = { id: string; title: string; level: number; };
import Link from 'next/link'; import { usePathname } from 'next/navigation'; const pathname = usePathname(); // /works, /skills, /contact
export const metadata = { title: 'Портфолио | SasWeb', description: 'Примеры работ frontend-разработки', };
.center-information__scroll { .simplebar-scrollbar ::before { background: linear-gradient( 180deg, var(--accent-start), var(--accent-end) ); } }
const { isClosing, requestClose } = useModalClose(onClose); createPortal( <div className="portfolio-modal"> <ContactForm /> </div>, document.body );
const handleSubmit = (e) => { e.preventDefault(); const { name, email, message } = form; // send to API / mail service };
:root { --accent-start: #00c897; --accent-end: #007bfd; --page-bg: #050a0e; } html.theme-light { --page-bg: #eef4f6; }
Воронежский государственный архитектурно-строительный университет, Воронеж
Платформа LearnJS
Дополнительное изучение React и его возможностей