feat: init

This commit is contained in:
Morten Olsen
2022-12-06 09:12:53 +01:00
commit 3f5e941446
115 changed files with 13148 additions and 0 deletions

View 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>
);
}

View 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;

View 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;

View 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;