mirror of
https://github.com/morten-olsen/morten-olsen.github.io.git
synced 2026-02-08 01:46:28 +01:00
feat: init
This commit is contained in:
58
webpage/src/pages/_app.tsx
Normal file
58
webpage/src/pages/_app.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
import type { AppProps } from 'next/app';
|
||||
import Head from 'next/head';
|
||||
import '@fontsource/merriweather';
|
||||
import '@fontsource/pacifico';
|
||||
import { ThemeProvider } from '../theme/provider';
|
||||
import { createTheme } from '../theme/create';
|
||||
import chroma from 'chroma-js';
|
||||
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h1, h2, h3,h4, h5, h6 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html, body, body > #__next {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Merriweather', sans-serif;
|
||||
background: ${({ theme }) => theme.colors.background};
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
const theme = useMemo(
|
||||
() =>
|
||||
createTheme({
|
||||
baseColor: chroma.random().brighten(1).hex(),
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<GlobalStyle />
|
||||
<Head>
|
||||
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
|
||||
</Head>
|
||||
<Component {...pageProps} />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
44
webpage/src/pages/_document.tsx
Normal file
44
webpage/src/pages/_document.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
||||
import { ServerStyleSheet } from 'styled-components';
|
||||
|
||||
class RootDocument extends Document {
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
static async getInitialProps(ctx: any) {
|
||||
const sheet = new ServerStyleSheet();
|
||||
const originalRenderPage = ctx.renderPage;
|
||||
|
||||
try {
|
||||
ctx.renderPage = () =>
|
||||
originalRenderPage({
|
||||
enhanceApp: (App: any) => (props: any) =>
|
||||
sheet.collectStyles(<App {...props} />),
|
||||
});
|
||||
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
return {
|
||||
...initialProps,
|
||||
styles: (
|
||||
<>
|
||||
{initialProps.styles}
|
||||
{sheet.getStyleElement()}
|
||||
</>
|
||||
),
|
||||
};
|
||||
} finally {
|
||||
sheet.seal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default RootDocument;
|
||||
176
webpage/src/pages/articles/[slug].tsx
Normal file
176
webpage/src/pages/articles/[slug].tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
import { getArticles } from '@morten-olsen/personal-webpage-articles';
|
||||
import { GetStaticPaths, GetStaticProps } from 'next';
|
||||
import styled from 'styled-components';
|
||||
import React from 'react';
|
||||
import { ReactMarkdown } from 'react-markdown/lib/react-markdown';
|
||||
import { Jumbo, Overline } from 'typography';
|
||||
|
||||
type Props = {
|
||||
article: ReturnType<typeof getArticles>[number];
|
||||
};
|
||||
|
||||
const Wrapper = styled.div``;
|
||||
|
||||
const ArticleWrapper = styled.article`
|
||||
margin-right: 40%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
background: ${({ theme }) => theme.colors.background};
|
||||
min-height: 100vh;
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
margin-right: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const ArticleContent = styled.div`
|
||||
max-width: 900px;
|
||||
padding: 50px;
|
||||
letter-spacing: 0.5px;
|
||||
line-height: 2.1rem;
|
||||
color: ${({ theme }) => theme.colors.foreground};
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
p:first-of-type::first-letter {
|
||||
font-size: 6rem;
|
||||
float: left;
|
||||
padding: 1rem;
|
||||
margin: 0px 2rem;
|
||||
font-weight: 100;
|
||||
margin-left: 0rem;
|
||||
}
|
||||
|
||||
p + p::first-letter {
|
||||
margin-left: 1.8rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-left: 50px;
|
||||
@media only screen and (max-width: 700px) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.colors.primary};
|
||||
background: ${({ theme }) => theme.colors.foreground};
|
||||
padding: 0.2rem 0.4rem;
|
||||
}
|
||||
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
text-transform: uppercase;
|
||||
margin: 5px 0;
|
||||
background: ${({ theme }) => theme.colors.primary};
|
||||
color: ${({ theme }) => theme.colors.foreground};
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const AsideWrapper = styled.aside<{
|
||||
image?: string;
|
||||
}>`
|
||||
width: 40%;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: ${({ theme }) => theme.colors.primary};
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
opacity: 0.5;
|
||||
${({ image }) => (image ? `background-image: url(${image});` : '')}
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
position: static;
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Title = styled(Jumbo)`
|
||||
font-size: 60px;
|
||||
line-height: 80px;
|
||||
display: inline-block;
|
||||
background: ${({ theme }) => theme.colors.primary};
|
||||
color: ${({ theme }) => theme.colors.foreground};
|
||||
padding: 0 15px;
|
||||
text-transform: uppercase;
|
||||
margin: 5px;
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
font-size: 2rem;
|
||||
line-height: 2.1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const Meta = styled(Overline)`
|
||||
font-size: 0.8rem;
|
||||
`;
|
||||
|
||||
const Article: React.FC<Props> = ({ article }) => {
|
||||
return (
|
||||
<Wrapper>
|
||||
<ArticleWrapper>
|
||||
<ArticleContent>
|
||||
{article.title.split(' ').map((word, index) => (
|
||||
<Title key={index}>{word}</Title>
|
||||
))}
|
||||
<div>
|
||||
<Meta>
|
||||
By Morten Olsen - 5 min read{' '}
|
||||
{article.pdf && (
|
||||
<a href={article.pdf} target="_blank">
|
||||
download as PDF
|
||||
</a>
|
||||
)}
|
||||
</Meta>
|
||||
</div>
|
||||
<AsideWrapper image={article.cover} />
|
||||
<ReactMarkdown>{article.content}</ReactMarkdown>
|
||||
</ArticleContent>
|
||||
</ArticleWrapper>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const getStaticProps: GetStaticProps<Props> = async (context) => {
|
||||
const { slug } = context.params;
|
||||
const articles = getArticles();
|
||||
const article = articles.find((a) => a.meta.slug === slug);
|
||||
|
||||
return {
|
||||
props: {
|
||||
article,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const getStaticPaths: GetStaticPaths = async () => {
|
||||
const articles = getArticles();
|
||||
return {
|
||||
paths: articles.map((a) => ({
|
||||
params: { slug: a.meta?.slug ?? a.title },
|
||||
})),
|
||||
fallback: false,
|
||||
};
|
||||
};
|
||||
|
||||
export { getStaticPaths, getStaticProps };
|
||||
export default Article;
|
||||
89
webpage/src/pages/index.tsx
Normal file
89
webpage/src/pages/index.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import React, { FC } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Jumbo } from 'typography';
|
||||
import { getArticles } from '@morten-olsen/personal-webpage-articles/dist/index';
|
||||
import { GetStaticProps } from 'next';
|
||||
import { getPositions, Position } from '@morten-olsen/personal-webpage-profile';
|
||||
import { Sheet } from '../components/sheet';
|
||||
import ArticlePreview from 'components/articles/preview';
|
||||
|
||||
type Props = {
|
||||
articles: ReturnType<typeof getArticles>;
|
||||
positions: Position[];
|
||||
};
|
||||
|
||||
const Hero = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const Title = styled(Jumbo)`
|
||||
font-size: 60px;
|
||||
line-height: 80px;
|
||||
display: inline-block;
|
||||
background: ${({ theme }) => theme.colors.primary};
|
||||
color: ${({ theme }) => theme.colors.foreground};
|
||||
padding: 0 15px;
|
||||
text-transform: uppercase;
|
||||
margin: 5px;
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
font-size: 2rem;
|
||||
line-height: 2.1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const ArticleList = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const Index: FC<Props> = ({ articles, positions }) => {
|
||||
return (
|
||||
<>
|
||||
<Sheet backgroundColor="red">
|
||||
<Hero>
|
||||
{"Hi, I'm Morten Olsen".split(' ').map((char, index) => (
|
||||
<Title key={index}>{char}</Title>
|
||||
))}
|
||||
</Hero>
|
||||
<Hero>
|
||||
{'And I make software'.split(' ').map((char, index) => (
|
||||
<Title key={index}>{char}</Title>
|
||||
))}
|
||||
</Hero>
|
||||
</Sheet>
|
||||
<Sheet backgroundColor="#273c75">
|
||||
<h2>Articles</h2>
|
||||
<ArticleList>
|
||||
{articles.map((article) => (
|
||||
<ArticlePreview key={article.title} article={article} />
|
||||
))}
|
||||
</ArticleList>
|
||||
</Sheet>
|
||||
<Sheet backgroundColor="red">
|
||||
{positions.map((position) => (
|
||||
<div>{position.attributes.title}</div>
|
||||
))}
|
||||
</Sheet>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<Props> = async () => {
|
||||
const articles = getArticles();
|
||||
const positions = getPositions();
|
||||
console.log(articles);
|
||||
return {
|
||||
props: {
|
||||
articles,
|
||||
positions,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default Index;
|
||||
Reference in New Issue
Block a user