@@ -46,7 +46,6 @@ | |||
"line-comment-position": "error", | |||
"lines-between-class-members": "error", | |||
"max-depth": ["error", 3], | |||
"max-lines-per-function": ["error", 50], | |||
"max-statements-per-line": "error", | |||
"multiline-comment-style": "error", | |||
"multiline-ternary": ["error", "always-multiline"], | |||
@@ -1,4 +1,5 @@ | |||
import { h, FunctionalComponent, Fragment } from "preact"; | |||
import { h, FunctionalComponent } from "preact"; | |||
import { useState } from "preact/hooks"; | |||
import { Router } from "preact-router"; | |||
import { | |||
faBalanceScale, | |||
@@ -8,74 +9,78 @@ import { | |||
faHome, | |||
} from "@fortawesome/free-solid-svg-icons"; | |||
import { layout, sidebar, Typography } from "teenyui"; | |||
import { Home } from "~pages/Home"; | |||
import { Overview } from "~pages/Overview"; | |||
import { Button } from "~pages/components/Button"; | |||
const { Container, Grid } = layout; | |||
const { Category, Item, Sidebar } = sidebar; | |||
const General: FunctionalComponent = () => ( | |||
<Fragment> | |||
<Category>General</Category> | |||
<Item href="/" title="Main page" icon={faHome}> | |||
Main | |||
</Item> | |||
<Item href="/overview" title="An overview of all components." icon={faEye}> | |||
Overview | |||
</Item> | |||
</Fragment> | |||
); | |||
const Components: FunctionalComponent = () => ( | |||
<Fragment> | |||
<Category>Components</Category> | |||
<Item href="/components/button" title="Button component" icon={faMouse}> | |||
Button | |||
</Item> | |||
</Fragment> | |||
); | |||
const { Container } = layout; | |||
const { Item, Sidebar } = sidebar; | |||
const Important: FunctionalComponent = () => ( | |||
<Fragment> | |||
<Category>Important Links</Category> | |||
<Item | |||
href="https://git.dany.dev/dsluijk/teenyui" | |||
title="TeenyUI Git page" | |||
icon={faBalanceScale} | |||
target="_blank" | |||
> | |||
TeenyUI | |||
</Item> | |||
<Item | |||
href="https://dany.dev" | |||
title="My website!" | |||
icon={faBolt} | |||
target="_blank" | |||
> | |||
Dany | |||
</Item> | |||
</Fragment> | |||
); | |||
export const Main: FunctionalComponent = () => { | |||
const [route, setRoute] = useState(""); | |||
const [open, setOpen] = useState(true); | |||
export const Main: FunctionalComponent = () => ( | |||
<Typography> | |||
<Container> | |||
<Grid size={4} mobileSize={12} desktopSize={2}> | |||
<Sidebar> | |||
<General /> | |||
<Components /> | |||
<Important /> | |||
return ( | |||
<Typography> | |||
<Container pillar> | |||
<Sidebar open={open}> | |||
<h3>General</h3> | |||
<Item | |||
href="/" | |||
title="Main page" | |||
icon={faHome} | |||
current={route} | |||
onClick={() => setOpen(false)} | |||
> | |||
Main | |||
</Item> | |||
<Item | |||
href="/overview" | |||
title="An overview of all components." | |||
icon={faEye} | |||
current={route} | |||
onClick={() => setOpen(false)} | |||
> | |||
Overview | |||
</Item> | |||
<h3>Components</h3> | |||
<Item | |||
href="/components/button" | |||
title="Button component" | |||
icon={faMouse} | |||
current={route} | |||
onClick={() => setOpen(false)} | |||
> | |||
Button | |||
</Item> | |||
<h3>Important Links</h3> | |||
<Item | |||
href="https://git.dany.dev/dsluijk/teenyui" | |||
title="TeenyUI Git page" | |||
icon={faBalanceScale} | |||
target="_blank" | |||
> | |||
TeenyUI | |||
</Item> | |||
<Item | |||
href="https://dany.dev" | |||
title="My website!" | |||
icon={faBolt} | |||
target="_blank" | |||
> | |||
About Me | |||
</Item> | |||
</Sidebar> | |||
</Grid> | |||
<Grid size={8} mobileSize={12} desktopSize={10}> | |||
<Router> | |||
<Home path="/" /> | |||
<Overview path="/overview" /> | |||
<Button path="/components/button" /> | |||
<div default>404!!1!</div> | |||
</Router> | |||
</Grid> | |||
</Container> | |||
</Typography> | |||
); | |||
<main> | |||
<Router onChange={(e) => setRoute(e.url)}> | |||
<Home path="/" /> | |||
<Overview path="/overview" /> | |||
<Button path="/components/button" /> | |||
<div default>404!!1!</div> | |||
</Router> | |||
</main> | |||
</Container> | |||
</Typography> | |||
); | |||
}; |
@@ -1,5 +1,7 @@ | |||
import { h, FunctionalComponent, Fragment } from "preact"; | |||
import { Hero, Button, Card } from "teenyui"; | |||
import { Button, card } from "teenyui"; | |||
const { Card, Hero } = card; | |||
export const Home: FunctionalComponent = () => ( | |||
<Fragment> | |||
@@ -9,7 +11,7 @@ export const Home: FunctionalComponent = () => ( | |||
imageLabel="Demo image" | |||
> | |||
<h1>Page Title</h1> | |||
<h4>Subtitle</h4> | |||
<h3>Subtitle</h3> | |||
<div> | |||
<Button>Button 1</Button> | |||
<Button ghost>Button 2</Button> | |||
@@ -1,9 +1,11 @@ | |||
import { h, FunctionalComponent } from "preact"; | |||
import { Card } from "teenyui"; | |||
import { card } from "teenyui"; | |||
const { Card } = card; | |||
export const Overview: FunctionalComponent = () => ( | |||
<Card> | |||
<h2>Overview</h2> | |||
<h1>Overview</h1> | |||
<p> | |||
There should be a general overview of all components here, but this has | |||
not been done yet. | |||
@@ -3,15 +3,16 @@ import { | |||
faBalanceScale, | |||
faBolt, | |||
faInfinity, | |||
faCookie, | |||
} from "@fortawesome/free-solid-svg-icons"; | |||
import { Button as ButtonComp, Card, Icon } from "teenyui"; | |||
import { Button as ButtonComp, card, Icon } from "teenyui"; | |||
import { timeout } from "~utils"; | |||
const { Card } = card; | |||
export const Button: FunctionalComponent = () => ( | |||
<Card> | |||
<h2>Button</h2> | |||
<h1>Button</h1> | |||
<h4>Clicky buttons are fun!</h4> | |||
<ButtonComp | |||
onClick={async () => { | |||
await timeout(5000); | |||
@@ -30,9 +31,6 @@ export const Button: FunctionalComponent = () => ( | |||
> | |||
Ghost Button | |||
</ButtonComp> | |||
<ButtonComp ghost inversed> | |||
Ghost Inversed Button | |||
</ButtonComp> | |||
<ButtonComp round> | |||
<Icon icon={faBalanceScale} /> | |||
</ButtonComp> | |||
@@ -42,8 +40,5 @@ export const Button: FunctionalComponent = () => ( | |||
<ButtonComp round ghost> | |||
<Icon icon={faInfinity} /> | |||
</ButtonComp> | |||
<ButtonComp round ghost inversed> | |||
<Icon icon={faCookie} /> | |||
</ButtonComp> | |||
</Card> | |||
); |
@@ -1,7 +1,8 @@ | |||
$breakpoints: ( | |||
"small": 576px, | |||
"medium": 768px, | |||
"large": 1250px, | |||
"small": 600px, | |||
"medium": 900px, | |||
"large": 1200px, | |||
"huge": 1800px, | |||
) !default; | |||
@mixin breakpoint($operator, $breakpoint) { | |||
@@ -9,8 +9,7 @@ export const Button: FunctionalComponent<RenderableProps<Props>> = (props) => { | |||
const secondary = props.secondary ? style.secondary : ""; | |||
const ghost = props.ghost ? style.ghost : ""; | |||
const round = props.round ? style.round : ""; | |||
const inversed = props.inversed ? style.inversed : ""; | |||
const styles = `${style.button} ${secondary} ${ghost} ${round} ${inversed}`; | |||
const styles = `${style.button} ${secondary} ${ghost} ${round}`; | |||
const clickListener = async (): Promise<void> => { | |||
if (!props.onClick) return; | |||
@@ -37,7 +36,6 @@ interface Props { | |||
secondary?: boolean; | |||
ghost?: boolean; | |||
round?: boolean; | |||
inversed?: boolean; | |||
onClick?: () => void | Promise<void>; | |||
} |
@@ -5,12 +5,12 @@ | |||
align-items: center; | |||
min-width: 20px; | |||
border: 2px solid var(--primary-color-highlight); | |||
border: 2px solid var(--primary-color); | |||
border-radius: 0.3em; | |||
color: var(--primary-color-text); | |||
background: var(--primary-color); | |||
backdrop-filter: blur(8px); | |||
background: rgba(var(--primary-color-rgb), 0.8); | |||
backdrop-filter: blur(16px); | |||
cursor: pointer; | |||
&:disabled { | |||
@@ -21,8 +21,8 @@ | |||
.secondary { | |||
color: var(--secondary-color-text); | |||
background: var(--secondary-color); | |||
border: 2px solid var(--secondary-color-highlight); | |||
background: rgba(var(--secondary-color-rgb), 0.8); | |||
border: 2px solid var(--secondary-color); | |||
} | |||
.round { | |||
@@ -30,16 +30,12 @@ | |||
} | |||
.ghost { | |||
color: var(--text-color); | |||
color: var(--text-color-header); | |||
background: rgba(var(--background-rgb), 0.1); | |||
border: 2px solid var(--text-color-header); | |||
background: none; | |||
font-weight: 500; | |||
&:disabled { | |||
opacity: 0.4; | |||
} | |||
&.inversed { | |||
color: var(--text-color-inverse); | |||
border: 2px solid var(--text-color-inverse-header); | |||
} | |||
} |
@@ -1,7 +1,7 @@ | |||
.card { | |||
margin: 12px; | |||
margin: 12px 6px; | |||
padding: 16px; | |||
border-radius: 0.3em; | |||
box-shadow: 0 3px 5px 0 var(--background-border); | |||
box-shadow: 0 2px 4px 0 var(--background-border); | |||
background-color: var(--background-secondary); | |||
} |
@@ -3,6 +3,8 @@ | |||
overflow: hidden; | |||
z-index: 100; | |||
min-height: 36vh; | |||
border-radius: 6px; | |||
margin: 12px 6px; | |||
} | |||
.image { |
@@ -0,0 +1,2 @@ | |||
export * from "./card"; | |||
export * from "./hero"; |
@@ -1,7 +1,6 @@ | |||
export * from "./button"; | |||
export * from "./card"; | |||
export * from "./hero"; | |||
export * from "./icon"; | |||
export * from "./typography"; | |||
export * as card from "./card"; | |||
export * as layout from "./layout"; | |||
export * as sidebar from "./sidebar"; |
@@ -7,6 +7,11 @@ | |||
height: 100%; | |||
width: 100%; | |||
margin: 0 auto; | |||
background: var(--background); | |||
> * { | |||
flex: 1; | |||
} | |||
} | |||
@include breakpoint(min, large) { | |||
@@ -1,7 +0,0 @@ | |||
import { h, FunctionalComponent } from "preact"; | |||
import style from "./style.scss"; | |||
export const Category: FunctionalComponent = (props) => ( | |||
<span class={style.category}>{props.children}</span> | |||
); |
@@ -1,8 +0,0 @@ | |||
.category { | |||
display: flex; | |||
align-items: center; | |||
margin: 18px 18px 6px; | |||
font-size: 0.9em; | |||
color: var(--text-color-header); | |||
font-family: var(--font); | |||
} |
@@ -1,3 +1,2 @@ | |||
export * from "./category"; | |||
export * from "./item"; | |||
export * from "./sidebar"; |
@@ -1,23 +1,22 @@ | |||
import { h, FunctionalComponent, RenderableProps, JSX } from "preact"; | |||
import { h, FunctionalComponent, RenderableProps } from "preact"; | |||
import { IconDefinition } from "@fortawesome/free-solid-svg-icons"; | |||
import { Icon } from "~/icon"; | |||
import style from "./style.scss"; | |||
export const Item: FunctionalComponent<RenderableProps<Props>> = (props) => { | |||
let icon: JSX.Element; | |||
if (props.icon) { | |||
icon = <Icon icon={props.icon} />; | |||
} | |||
const active = props.current === props.href ? style.active : ""; | |||
const styles = `${style.item} ${active}`; | |||
return ( | |||
<a | |||
class={style.item} | |||
class={styles} | |||
href={props.href} | |||
title={props.title} | |||
target={props.target} | |||
onClick={() => props.onClick?.()} | |||
> | |||
{icon} | |||
<Icon icon={props.icon} /> | |||
{props.children} | |||
</a> | |||
); | |||
@@ -26,6 +25,8 @@ export const Item: FunctionalComponent<RenderableProps<Props>> = (props) => { | |||
interface Props { | |||
href: string; | |||
title: string; | |||
icon?: IconDefinition; | |||
icon: IconDefinition; | |||
current?: string; | |||
target?: "_blank" | "_parent" | "_top"; | |||
onClick?: () => void; | |||
} |
@@ -1,18 +1,29 @@ | |||
.item { | |||
height: 2.8em; | |||
padding: 0 18px; | |||
height: 2.4em; | |||
margin: 4px 18px; | |||
border-radius: 6px; | |||
display: flex; | |||
align-items: center; | |||
color: var(--text-color); | |||
font-family: var(--font); | |||
text-decoration: none; | |||
svg { | |||
color: var(--text-color-header); | |||
width: 2em; | |||
margin: 0 6px 0 0; | |||
margin-right: 4px; | |||
} | |||
&:hover { | |||
background: var(--background); | |||
&:hover:not(.active) { | |||
color: var(--text-color-header); | |||
background: var(--background-secondary); | |||
} | |||
} | |||
.active { | |||
background: rgba(var(--primary-color-rgb), 0.25); | |||
color: rgba(var(--primary-color-rgb), 0.95); | |||
svg { | |||
color: var(--primary-color); | |||
} | |||
} |
@@ -1,7 +1,12 @@ | |||
import { h, FunctionalComponent } from "preact"; | |||
import { h, FunctionalComponent, RenderableProps } from "preact"; | |||
import style from "./style.scss"; | |||
export const Sidebar: FunctionalComponent = (props) => ( | |||
<nav class={style.sidebar}>{props.children}</nav> | |||
); | |||
export const Sidebar: FunctionalComponent<RenderableProps<Props>> = (props) => { | |||
const styles = `${style.sidebar} ${props.open && style.open}`; | |||
return <nav class={styles}>{props.children}</nav>; | |||
}; | |||
interface Props { | |||
open: boolean; | |||
} |
@@ -1,6 +1,53 @@ | |||
@import "../../mixins"; | |||
.sidebar { | |||
height: 100%; | |||
display: flex; | |||
position: absolute; | |||
display: none; | |||
flex-direction: column; | |||
background: var(--background-secondary); | |||
width: 100vw; | |||
height: 100vh; | |||
flex: 0 0 100vw; | |||
z-index: 1001; | |||
backdrop-filter: blur(24px); | |||
background: rgba(var(--background-rgb), 0.6); | |||
@supports not (backdrop-filter: blur(12px)) { | |||
background: var(--background); | |||
} | |||
@include breakpoint(min, small) { | |||
display: flex; | |||
position: static; | |||
width: 42vw; | |||
flex: 0 0 42vw; | |||
height: 100%; | |||
} | |||
@include breakpoint(min, medium) { | |||
width: 33vw; | |||
flex: 0 0 33vw; | |||
} | |||
@include breakpoint(min, large) { | |||
width: 14vw; | |||
flex: 0 0 14vw; | |||
} | |||
@include breakpoint(min, huge) { | |||
width: 12vw; | |||
flex: 0 0 12vw; | |||
} | |||
h3 { | |||
margin: 12px 18px 4px; | |||
font-weight: lighter; | |||
font-size: 0.7em; | |||
text-transform: uppercase; | |||
color: var(--text-color-header); | |||
font-family: var(--font); | |||
} | |||
} | |||
.open { | |||
display: flex; | |||
} |
@@ -7,52 +7,52 @@ | |||
font-family: var(--font); | |||
h1 { | |||
font-size: 4em; | |||
font-weight: lighter; | |||
letter-spacing: -1.5px; | |||
font-size: 2.8em; | |||
font-weight: 250; | |||
letter-spacing: -1.2px; | |||
} | |||
h2 { | |||
font-size: 3.5em; | |||
font-weight: lighter; | |||
letter-spacing: -1.5px; | |||
font-size: 2.4em; | |||
font-weight: 250; | |||
letter-spacing: -1.2px; | |||
} | |||
h3 { | |||
font-size: 2.5em; | |||
font-size: 1.8em; | |||
font-weight: normal; | |||
letter-spacing: 0; | |||
} | |||
h4 { | |||
font-size: 2em; | |||
font-size: 1.4em; | |||
font-weight: normal; | |||
letter-spacing: 0.25px; | |||
} | |||
h5 { | |||
font-size: 1.5em; | |||
font-size: 1em; | |||
font-weight: normal; | |||
letter-spacing: 0; | |||
} | |||
h6 { | |||
font-size: 1.2em; | |||
font-size: 0.8em; | |||
font-weight: normal; | |||
letter-spacing: 0.15px; | |||
} | |||
h1, | |||
h3, | |||
h2, | |||
h5 { | |||
margin: 0 0 6px; | |||
margin: 0 0 2px; | |||
color: var(--text-color-header); | |||
} | |||
h2, | |||
h3, | |||
h4, | |||
h6 { | |||
margin: 0 0 8px; | |||
margin: 0 0 4px; | |||
color: var(--text-color-subheader); | |||
} | |||
@@ -1,19 +1,17 @@ | |||
html { | |||
--primary-color: rgb(88, 90, 218); | |||
--primary-color-highlight: rgb(39, 42, 176); | |||
--primary-color: #272ab0; | |||
--primary-color-rgb: 39, 42, 176; | |||
--primary-color-text: #f1f3ff; | |||
--secondary-color: #eb5e28; | |||
--secondary-color-highlight: #e04e15; | |||
--secondary-color: #e04e15; | |||
--secondary-color-rgb: 224, 78, 21; | |||
--secondary-color-text: #ffffdb; | |||
--text-color: #000; | |||
--text-color-header: #191919; | |||
--text-color-subheader: #2f2f2f; | |||
--text-color-inverse: #fff; | |||
--text-color-inverse-header: #eaf4f4; | |||
--text-color-inverse-subheader: #eaf4f4; | |||
--background: #f1f5fb; | |||
--text-color: #585757; | |||
--text-color-header: #1c1c1c; | |||
--text-color-subheader: #969696; | |||
--background: #f7f7f7; | |||
--background-rgb: 247, 247, 247; | |||
--background-secondary: #fff; | |||
--background-border: #ccd0d9; | |||
--background-border: #d6d6d6; | |||
--font: "Open Sans", Arial, Helvetica, sans-serif; | |||
} |