mirror of
https://github.com/kremalicious/blog.git
synced 2024-12-22 09:13:35 +01:00
Merge pull request #173 from kremalicious/feature/dark-mode
add dark mode
This commit is contained in:
commit
df1438e6b3
@ -22,6 +22,7 @@
|
||||
- [🔍 Search](#-search)
|
||||
- [🕸 Related Posts](#-related-posts)
|
||||
- [📝 GitHub changelog rendering](#-github-changelog-rendering)
|
||||
- [🌗 Theme Switcher](#-theme-switcher)
|
||||
- [🏆 SEO component](#-seo-component)
|
||||
- [📈 Matomo (formerly Piwik) analytics tracking](#-matomo-formerly-piwik-analytics-tracking)
|
||||
- [gatsby-redirect-from](#gatsby-redirect-from)
|
||||
@ -117,6 +118,14 @@ If you want to know how this works, have a look at the respective component unde
|
||||
|
||||
- [`src/components/atoms/Changelog.jsx`](src/components/atoms/Changelog.jsx)
|
||||
|
||||
### 🌗 Theme Switcher
|
||||
|
||||
Includes a theme switcher which allows user to toggle between a light and a dark theme. Switching between them also happens automatically based on user's system preferences utilizing [use-dark-mode](https://github.com/donavon/use-dark-mode).
|
||||
|
||||
If you want to know how, have a look at the respective components:
|
||||
|
||||
- [`src/components/molecules/ThemeSwitch.jsx`](src/components/molecules/ThemeSwitch.jsx)
|
||||
|
||||
### 🏆 SEO component
|
||||
|
||||
Includes a SEO component which automatically switches all required `meta` tags for search engines, Twitter Cards, and Facebook OpenGraph tags based on the browsed route/page.
|
||||
|
@ -38,7 +38,8 @@ module.exports = {
|
||||
withWebp: true,
|
||||
linkImagesToOriginal: true,
|
||||
showCaptions: true,
|
||||
backgroundColor: '#e7eef4'
|
||||
backgroundColor: 'transparent',
|
||||
disableBgImageOnAlpha: true
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -208,6 +209,14 @@ module.exports = {
|
||||
exclude: ['/page/*', '/tags/*']
|
||||
}
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-plugin-use-dark-mode',
|
||||
options: {
|
||||
classNameDark: 'dark',
|
||||
classNameLight: 'light',
|
||||
minify: true
|
||||
}
|
||||
},
|
||||
'gatsby-plugin-webpack-size',
|
||||
'gatsby-plugin-react-helmet',
|
||||
'gatsby-plugin-catch-links',
|
||||
|
@ -28,6 +28,7 @@
|
||||
"not op_mini all"
|
||||
],
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.6",
|
||||
"dms2dec": "^1.1.0",
|
||||
"fast-exif": "^1.0.1",
|
||||
"fraction.js": "^4.0.12",
|
||||
@ -46,6 +47,7 @@
|
||||
"gatsby-plugin-sitemap": "^2.2.16",
|
||||
"gatsby-plugin-svgr": "^2.0.2",
|
||||
"gatsby-plugin-typescript": "^2.1.11",
|
||||
"gatsby-plugin-use-dark-mode": "^1.1.2",
|
||||
"gatsby-plugin-webpack-size": "^1.0.0",
|
||||
"gatsby-redirect-from": "^0.2.1",
|
||||
"gatsby-remark-autolink-headers": "^2.1.13",
|
||||
@ -76,6 +78,7 @@
|
||||
"remark": "^11.0.1",
|
||||
"remark-react": "^6.0.0",
|
||||
"slugify": "^1.3.4",
|
||||
"use-dark-mode": "^2.3.1",
|
||||
"web3": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -85,6 +88,7 @@
|
||||
"@svgr/webpack": "^4.3.3",
|
||||
"@testing-library/jest-dom": "^4.1.0",
|
||||
"@testing-library/react": "^9.2.0",
|
||||
"@types/classnames": "^2.2.9",
|
||||
"@types/jest": "^24.0.18",
|
||||
"@types/node": "^12.7.8",
|
||||
"@types/react": "^16.9.4",
|
||||
|
@ -22,22 +22,31 @@
|
||||
/////////////////////////////////////
|
||||
|
||||
.document {
|
||||
@include transition;
|
||||
|
||||
width: 100%;
|
||||
padding-top: ($spacer * 2);
|
||||
background-color: $page-background-color;
|
||||
background-color: $body-background-color;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.7);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.7);
|
||||
padding-bottom: $spacer * 2;
|
||||
box-shadow: 0 1px 4px rgba($brand-main, 0.1),
|
||||
0 -1px 4px rgba($brand-main, 0.2);
|
||||
transform: translate3d(0, 0, 0);
|
||||
transition: 0.4s $easing;
|
||||
transition-property: transform, background;
|
||||
|
||||
:global(.has-menu-open) & {
|
||||
transform: translate3d(0, ($spacer * 3), 0);
|
||||
}
|
||||
|
||||
:global(.dark) & {
|
||||
background-color: $body-background-color--dark;
|
||||
color: $text-color--dark;
|
||||
border-top-color: darken($brand-grey, 15%);
|
||||
border-bottom-color: darken($brand-grey, 15%);
|
||||
box-shadow: 0 1px 4px darken($brand-main, 10%),
|
||||
0 -1px 4px darken($brand-main, 15%);
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm) and (min-height: 500px) {
|
||||
margin-top: $spacer * 2.65;
|
||||
margin-bottom: $spacer * 19; // height of footer
|
||||
|
@ -13,6 +13,10 @@
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
|
||||
:global(.dark) & {
|
||||
background: darken($body-background-color--dark, 2%);
|
||||
}
|
||||
|
||||
@media (min-width: $screen-md) {
|
||||
margin-left: -100%;
|
||||
margin-right: -18%;
|
||||
|
@ -15,7 +15,7 @@
|
||||
top: 10%;
|
||||
padding: $spacer / 3 $spacer;
|
||||
background: rgba($link-color, 0.85);
|
||||
color: #fff;
|
||||
color: #fff !important;
|
||||
text-shadow: 0 1px 0 #000;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
|
@ -9,6 +9,10 @@
|
||||
color: $color-headings;
|
||||
margin-top: 0;
|
||||
margin-bottom: $spacer;
|
||||
|
||||
:global(.dark) & {
|
||||
color: $color-headings--dark;
|
||||
}
|
||||
}
|
||||
|
||||
.hentry__title__link {
|
||||
|
@ -15,6 +15,10 @@
|
||||
-webkit-overflow-scrolling: touch;
|
||||
height: 91vh;
|
||||
|
||||
:global(.dark) & {
|
||||
background: rgba($body-background-color--dark, 0.95);
|
||||
}
|
||||
|
||||
ul {
|
||||
@include breakoutviewport;
|
||||
|
||||
|
@ -38,7 +38,7 @@ export default function Search({ lng }: { lng: string }) {
|
||||
{searchOpen && (
|
||||
<>
|
||||
<Helmet>
|
||||
<body className="hasSearchOpen" />
|
||||
<html className="hasSearchOpen" lang="en" />
|
||||
</Helmet>
|
||||
|
||||
<CSSTransition
|
||||
|
@ -11,6 +11,10 @@
|
||||
margin-left: $spacer / 2;
|
||||
border-left: 1px solid $brand-grey-dimmed;
|
||||
|
||||
:global(.dark) & {
|
||||
border-left-color: darken($body-background-color--dark, 5%);
|
||||
}
|
||||
|
||||
h2 {
|
||||
position: relative;
|
||||
|
||||
|
@ -22,18 +22,22 @@
|
||||
flex: 1 1 20%;
|
||||
white-space: nowrap;
|
||||
padding: $spacer / 1.5;
|
||||
border-bottom: 1px solid $brand-grey-dimmed;
|
||||
border-bottom: 1px dashed $brand-grey-dimmed;
|
||||
|
||||
&:first-child {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
:global(.dark) & {
|
||||
border-bottom-color: $brand-grey;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm) {
|
||||
margin-bottom: 0;
|
||||
|
||||
span {
|
||||
border-left: 1px solid $brand-grey-dimmed;
|
||||
border-left: 1px dashed $brand-grey-dimmed;
|
||||
border-bottom: 0;
|
||||
padding: $spacer;
|
||||
|
||||
@ -45,6 +49,10 @@
|
||||
&:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
:global(.dark) & {
|
||||
border-left-color: $brand-grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ const MAPBOX_ACCESS_TOKEN =
|
||||
const retina =
|
||||
typeof window !== 'undefined' && window.devicePixelRatio >= 2 ? '@2x' : ''
|
||||
|
||||
const isDarkMode =
|
||||
typeof window !== 'undefined' && document.body.classList.contains('dark')
|
||||
|
||||
const mapbox = (mapboxId: string, accessToken: string) => (
|
||||
x: string,
|
||||
y: string,
|
||||
@ -50,7 +53,7 @@ export default function ExifMap({
|
||||
zoom={zoom}
|
||||
height={160}
|
||||
attribution={false}
|
||||
provider={providers['light']}
|
||||
provider={isDarkMode ? providers['dark'] : providers['light']}
|
||||
metaWheelZoom={true}
|
||||
metaWheelZoomWarning={'META+wheel to zoom'}
|
||||
>
|
||||
|
@ -14,6 +14,10 @@
|
||||
animation: fadein 0.3s;
|
||||
padding: $spacer;
|
||||
|
||||
:global(.dark) & {
|
||||
background: rgba($body-background-color--dark, 0.95);
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm) {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@ -24,7 +28,7 @@
|
||||
|
||||
.modal__content {
|
||||
outline: 0;
|
||||
background: transparent;
|
||||
background: $body-background-color;
|
||||
position: relative;
|
||||
border-radius: $border-radius;
|
||||
border: 1px solid rgba($brand-grey-light, 0.4);
|
||||
@ -32,6 +36,11 @@
|
||||
padding: 0 $spacer / 2 $spacer / 2;
|
||||
max-width: 100%;
|
||||
|
||||
:global(.dark) & {
|
||||
background: $body-background-color--dark;
|
||||
box-shadow: 0 5px 30px rgba(darken($brand-main, 20%), 0.5);
|
||||
}
|
||||
|
||||
@media (min-width: $screen-md) {
|
||||
max-width: $screen-sm;
|
||||
padding: 0 $spacer $spacer;
|
||||
|
@ -35,7 +35,7 @@
|
||||
text-align: right;
|
||||
padding: $spacer / 3;
|
||||
background: rgba($link-color, 0.85);
|
||||
color: #fff;
|
||||
color: #fff !important;
|
||||
text-shadow: 0 1px 0 #000;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
|
@ -43,5 +43,9 @@
|
||||
padding: $padding-base-horizontal;
|
||||
display: block;
|
||||
text-align: center;
|
||||
|
||||
:global(.dark) & {
|
||||
text-shadow: 0 -1px 0 rgba(#000, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ export default function Menu() {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<body className={menuOpen ? 'has-menu-open' : null} />
|
||||
<html className={menuOpen ? 'has-menu-open' : null} lang="en" />
|
||||
</Helmet>
|
||||
<Hamburger onClick={toggleMenu} />
|
||||
<ul className={styles.menu}>{MenuItems}</ul>
|
||||
|
@ -13,6 +13,10 @@
|
||||
margin-bottom: $spacer / 2;
|
||||
color: $brand-grey;
|
||||
text-transform: capitalize;
|
||||
|
||||
:global(.dark) & {
|
||||
color: $brand-grey-light;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
@ -20,6 +24,7 @@
|
||||
text-align: center;
|
||||
margin-bottom: $spacer;
|
||||
|
||||
// stylelint-disable-next-line no-descending-specificity
|
||||
h4 {
|
||||
font-size: $font-size-large;
|
||||
margin-top: 0;
|
||||
@ -28,6 +33,10 @@
|
||||
|
||||
p {
|
||||
color: $brand-grey-light;
|
||||
|
||||
:global(.dark) & {
|
||||
color: $brand-grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
84
src/components/molecules/ThemeSwitch.module.scss
Normal file
84
src/components/molecules/ThemeSwitch.module.scss
Normal file
@ -0,0 +1,84 @@
|
||||
@import 'variables';
|
||||
@import 'mixins';
|
||||
|
||||
.themeSwitch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-right: $spacer;
|
||||
opacity: 0.75;
|
||||
bottom: -0.1rem;
|
||||
|
||||
svg {
|
||||
width: 0.8rem;
|
||||
height: 0.8rem;
|
||||
margin-top: -0.05rem;
|
||||
fill: $brand-grey-light;
|
||||
|
||||
&:last-child {
|
||||
margin-top: -0.1rem;
|
||||
width: 0.7rem;
|
||||
height: 0.7rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkboxContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
$knob-size: 8px;
|
||||
$knob-space: 1px;
|
||||
|
||||
.checkboxFake {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: ($knob-size + ($knob-space * 2)) * 2;
|
||||
height: $knob-size + ($knob-space * 4);
|
||||
border: 1px solid $brand-grey-light;
|
||||
border-radius: 15rem;
|
||||
margin-left: $spacer / 3;
|
||||
margin-right: $spacer / 3;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: $knob-space;
|
||||
left: $knob-space;
|
||||
width: $knob-size;
|
||||
height: $knob-size;
|
||||
background-color: $brand-grey-light;
|
||||
border-radius: 15rem;
|
||||
transition: transform 0.2s $easing;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
[type='checkbox'],
|
||||
.label {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
[type='checkbox'] {
|
||||
&:checked {
|
||||
+ .checkboxContainer {
|
||||
.checkboxFake {
|
||||
&::after {
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
src/components/molecules/ThemeSwitch.test.tsx
Normal file
24
src/components/molecules/ThemeSwitch.test.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react'
|
||||
import { render, fireEvent, cleanup } from '@testing-library/react'
|
||||
import ThemeSwitch from './ThemeSwitch'
|
||||
|
||||
describe('ThemeSwitch', () => {
|
||||
afterEach(cleanup)
|
||||
|
||||
it('renders correctly', () => {
|
||||
const { container } = render(<ThemeSwitch />)
|
||||
const switchContainer = container.querySelector('aside')
|
||||
expect(switchContainer).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('checkbox can be changed', () => {
|
||||
const { container } = render(<ThemeSwitch />)
|
||||
|
||||
const toggle = container.querySelector('input')
|
||||
const label = container.querySelector('label')
|
||||
expect(toggle.checked).toBeFalsy()
|
||||
fireEvent.click(label)
|
||||
fireEvent.change(toggle, { target: { checked: true } })
|
||||
expect(toggle.checked).toBeTruthy()
|
||||
})
|
||||
})
|
55
src/components/molecules/ThemeSwitch.tsx
Normal file
55
src/components/molecules/ThemeSwitch.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import React from 'react'
|
||||
import useDarkMode from 'use-dark-mode'
|
||||
|
||||
import { ReactComponent as Day } from '../../images/day.svg'
|
||||
import { ReactComponent as Night } from '../../images/night.svg'
|
||||
import styles from './ThemeSwitch.module.scss'
|
||||
|
||||
const ThemeToggle = () => (
|
||||
<span id="toggle" className={styles.checkboxContainer} aria-live="assertive">
|
||||
<Day />
|
||||
<span className={styles.checkboxFake} />
|
||||
<Night />
|
||||
</span>
|
||||
)
|
||||
|
||||
const ThemeToggleInput = ({
|
||||
isDark,
|
||||
toggleDark
|
||||
}: {
|
||||
isDark: boolean
|
||||
toggleDark(): void
|
||||
}) => (
|
||||
<input
|
||||
onChange={toggleDark}
|
||||
type="checkbox"
|
||||
name="toggle"
|
||||
value="toggle"
|
||||
aria-describedby="toggle"
|
||||
checked={isDark}
|
||||
/>
|
||||
)
|
||||
|
||||
export default function ThemeSwitch() {
|
||||
const darkMode = useDarkMode(false, {
|
||||
classNameDark: 'dark',
|
||||
classNameLight: 'light'
|
||||
})
|
||||
|
||||
return (
|
||||
<aside className={styles.themeSwitch}>
|
||||
<label
|
||||
htmlFor="toggle"
|
||||
className={styles.checkbox}
|
||||
onClick={darkMode.toggle}
|
||||
>
|
||||
<span className={styles.label}>Toggle Dark Mode</span>
|
||||
<ThemeToggleInput
|
||||
isDark={darkMode.value}
|
||||
toggleDark={darkMode.toggle}
|
||||
/>
|
||||
<ThemeToggle />
|
||||
</label>
|
||||
</aside>
|
||||
)
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.header__content {
|
||||
.headerContent {
|
||||
@include breakoutviewport;
|
||||
|
||||
position: relative;
|
||||
@ -31,56 +31,28 @@
|
||||
// Logo
|
||||
/////////////////////////////////////
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
background-image: url('../../images/logo.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: left top;
|
||||
width: 47px;
|
||||
height: 31px;
|
||||
|
||||
@media (min-width: $screen-sm) {
|
||||
width: 183px;
|
||||
}
|
||||
|
||||
@media (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
|
||||
background-image: url('../../images/logo@2x.png');
|
||||
background-size: 183px 62px;
|
||||
}
|
||||
|
||||
@media (-webkit-min-device-pixel-ratio: 3), (min-resolution: 344dpi) {
|
||||
background-image: url('../../images/logo@3x.png');
|
||||
background-size: 183px 62px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: $spacer / 5;
|
||||
margin-bottom: 0;
|
||||
// display toned down logo
|
||||
// by default
|
||||
@extend .logo;
|
||||
}
|
||||
|
||||
.header__logo {
|
||||
@include hide-text;
|
||||
// repeat logo
|
||||
// but display hover version
|
||||
@extend .logo;
|
||||
|
||||
background-position: left bottom;
|
||||
|
||||
// hide by default
|
||||
opacity: 0;
|
||||
// show smooooothly on hover
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
svg {
|
||||
fill: $brand-grey-light;
|
||||
width: 160px;
|
||||
height: 30px;
|
||||
margin: 0;
|
||||
transition: 0.2s $easing;
|
||||
}
|
||||
|
||||
&:active {
|
||||
top: 0;
|
||||
box-shadow: none;
|
||||
a {
|
||||
display: block;
|
||||
@include hide-text;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
svg {
|
||||
fill: $brand-cyan;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@ import { Link } from 'gatsby'
|
||||
import Container from '../atoms/Container'
|
||||
import Search from '../Search'
|
||||
import Menu from '../molecules/Menu'
|
||||
import ThemeSwitch from '../molecules/ThemeSwitch'
|
||||
import { ReactComponent as Logo } from '../../images/logo.svg'
|
||||
|
||||
import styles from './Header.module.scss'
|
||||
|
||||
@ -10,14 +12,15 @@ export default function Header() {
|
||||
return (
|
||||
<header role="banner" className={styles.header}>
|
||||
<Container>
|
||||
<div className={styles.header__content}>
|
||||
<div className={styles.headerContent}>
|
||||
<h1 className={styles.title}>
|
||||
<Link className={styles.header__logo} to="/">
|
||||
kremalicious
|
||||
<Link to="/">
|
||||
<Logo /> kremalicious
|
||||
</Link>
|
||||
</h1>
|
||||
|
||||
<nav role="navigation" className={styles.nav}>
|
||||
<ThemeSwitch />
|
||||
<Search lng="en" />
|
||||
<Menu />
|
||||
</nav>
|
||||
|
3
src/images/day.svg
Normal file
3
src/images/day.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21">
|
||||
<path d="M1001,1344.5 C1001,1347.53669 998.535313,1350 995.5,1350 C992.459875,1350 990,1347.53669 990,1344.5 C990,1341.46331 992.459875,1339 995.5,1339 C998.535313,1339 1001,1341.46331 1001,1344.5 Z M995.5,1336.484 C994.633125,1336.484 993.81625,1336.69175 993.035,1337 L993,1337 L995.5,1334 L998,1337 L997.96125,1337 C997.181875,1336.691 996.365,1336.484 995.5,1336.484 Z M995.5,1352.516 C996.365,1352.516 997.181875,1352.30825 997.96125,1352 L998,1352 L995.5,1355 L993,1352 L993.035,1352 C993.81625,1352.309 994.633125,1352.516 995.5,1352.516 Z M1003.516,1344.5 C1003.516,1343.63562 1003.3045,1342.81562 1003,1342.03625 L1003,1342 L1006,1344.5 L1003,1347 L1003,1346.96375 C1003.30525,1346.18438 1003.516,1345.36438 1003.516,1344.5 Z M987.484,1344.5 C987.484,1345.36438 987.69025,1346.18438 988,1346.96375 L988,1347 L985,1344.5 L988,1342 L988,1342.03625 C987.6895,1342.81562 987.484,1343.63562 987.484,1344.5 Z M1001.34229,1350.34229 C1002.03819,1349.65134 1002.55304,1348.85785 1002.96959,1348.0297 L1003,1348 L1003,1352 L999,1352 L999.028289,1351.97242 C999.856436,1351.55233 1000.65205,1351.03819 1001.34229,1350.34229 Z M989.657001,1338.65771 C988.961103,1339.34866 988.447666,1340.14215 988.028289,1340.9703 L988,1341 L988,1337 L992,1337 L991.966761,1337.02758 C991.137907,1337.44767 990.348656,1337.96181 989.657001,1338.65771 Z M989.657709,1350.343 C990.349364,1351.0389 991.138614,1351.55304 991.966761,1351.97242 L992,1352 L988,1352 L988,1348 L988.028289,1348.0297 C988.448373,1348.85856 988.96181,1349.65205 989.657709,1350.343 Z M1001.34229,1338.657 C1000.65205,1337.9611 999.856436,1337.44696 999.028289,1337.02758 L999,1337 L1003,1337 L1003,1341 L1002.96959,1340.9703 C1002.55304,1340.14144 1002.03819,1339.34795 1001.34229,1338.657 Z" transform="translate(-985 -1334)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 10 KiB |
3
src/images/logo.svg
Normal file
3
src/images/logo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
Before Width: | Height: | Size: 21 KiB |
Binary file not shown.
Before Width: | Height: | Size: 37 KiB |
3
src/images/night.svg
Normal file
3
src/images/night.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="17" viewBox="0 0 18 17">
|
||||
<path d="M1057.35991,1345.6935 C1052.91908,1345.6935 1049.32216,1342.19489 1049.32216,1337.88132 C1049.32216,1336.46068 1049.74141,1335.14652 1050.42369,1334 C1046.72047,1335.03741 1044,1338.31568 1044,1342.24655 C1044,1347.00713 1047.97006,1350.86789 1052.86864,1350.86789 C1056.91247,1350.86789 1060.28811,1348.22007 1061.35547,1344.62446 C1060.17313,1345.28549 1058.82157,1345.6935 1057.35991,1345.6935 Z" transform="translate(-1044 -1334)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 539 B |
@ -97,6 +97,14 @@
|
||||
width: 100%;
|
||||
border-bottom: 1px dashed #fff;
|
||||
}
|
||||
|
||||
:global(.dark) & {
|
||||
border-bottom-color: darken($brand-main, 25%);
|
||||
|
||||
&::before {
|
||||
border-bottom-color: darken($brand-grey, 12%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin divider-top() {
|
||||
@ -114,6 +122,14 @@
|
||||
width: 100%;
|
||||
border-bottom: 1px dashed #fff;
|
||||
}
|
||||
|
||||
:global(.dark) & {
|
||||
border-top-color: darken($brand-main, 25%);
|
||||
|
||||
&::after {
|
||||
border-bottom-color: darken($brand-grey, 12%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Heading band
|
||||
@ -124,6 +140,10 @@
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
padding: ($spacer/2) $spacer ($spacer/2) 100%;
|
||||
margin-left: -100%;
|
||||
|
||||
:global(.dark) & {
|
||||
background: darken($body-background-color--dark, 2%);
|
||||
}
|
||||
}
|
||||
|
||||
// Layout breakout
|
||||
@ -224,6 +244,11 @@
|
||||
border-radius: $border-radius;
|
||||
box-shadow: 0 1px 3px rgba($brand-grey, 0.2);
|
||||
|
||||
:global(.dark) & {
|
||||
box-shadow: 0 3px 5px rgba(darken($brand-main, 20%), 0.15),
|
||||
0 5px 16px rgba(darken($brand-main, 20%), 0.15);
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm) {
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
@ -23,15 +23,13 @@ $alert-error: #f2dede;
|
||||
$body-background-color: $brand-light;
|
||||
$body-background-color--dark: darken($brand-grey, 22%);
|
||||
|
||||
$page-background-color: $brand-light;
|
||||
|
||||
// Text Colors
|
||||
/////////////////////////////////////
|
||||
|
||||
$text-color: $brand-grey;
|
||||
$text-color-light: $brand-grey-light;
|
||||
|
||||
$text-color--dark: lighten($brand-grey-light, 5%);
|
||||
$text-color--dark: lighten($brand-grey-light, 10%);
|
||||
$text-color-light--dark: lighten($brand-grey-light, 5%);
|
||||
|
||||
$link-color: $brand-cyan;
|
||||
|
@ -13,7 +13,6 @@ html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: $body-background-color;
|
||||
}
|
||||
|
||||
html {
|
||||
@ -32,7 +31,8 @@ body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
min-height: 100vh;
|
||||
transition: background 0.6s $easing;
|
||||
transition: background 0.4s $easing;
|
||||
background: $body-background-color;
|
||||
|
||||
// handling long text, like URLs
|
||||
overflow-wrap: break-word;
|
||||
@ -82,6 +82,26 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
// Links
|
||||
/////////////////////////////////////
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
text-decoration: none;
|
||||
transition: 0.2s ease-out;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
outline: 0;
|
||||
color: $link-color-hover;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transition: none;
|
||||
color: $link-color-active;
|
||||
}
|
||||
}
|
||||
|
||||
// Headings
|
||||
/////////////////////////////////////
|
||||
|
||||
@ -153,38 +173,19 @@ h5,
|
||||
h6 {
|
||||
font-family: $font-family-headings;
|
||||
line-height: $line-height-headings;
|
||||
color: $color-headings;
|
||||
font-weight: $font-weight-headings;
|
||||
letter-spacing: -0.02em;
|
||||
|
||||
.dark & {
|
||||
color: $color-headings--dark;
|
||||
}
|
||||
}
|
||||
|
||||
// Links
|
||||
/////////////////////////////////////
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
text-decoration: none;
|
||||
transition: 0.2s ease-out;
|
||||
|
||||
h1 &,
|
||||
h2 &,
|
||||
h3 & {
|
||||
// stylelint-disable no-descending-specificity
|
||||
&,
|
||||
a {
|
||||
color: $color-headings;
|
||||
}
|
||||
// stylelint-enable no-descending-specificity
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
outline: 0;
|
||||
color: $link-color-hover;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transition: none;
|
||||
color: $link-color-active;
|
||||
.dark &,
|
||||
.dark & a {
|
||||
color: $color-headings--dark;
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,6 +216,10 @@ figcaption {
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
margin-top: -($spacer / $line-height);
|
||||
|
||||
.dark & {
|
||||
color: $brand-grey-light;
|
||||
}
|
||||
}
|
||||
|
||||
// Lists
|
||||
|
Loading…
Reference in New Issue
Block a user