add initial contrast theme
This commit is contained in:
1 parent
c4e13e8e1b
commit
3fa99b28de
15 files changed
+168
-20
No files matched your search
+4
-1
@@ -8,6 +8,7 @@ import App from "next/app"
|
|||||||
import Head from "next/head"
|
import Head from "next/head"
|
||||||
import { YMetrika } from "src/components/YMetrika"
|
import { YMetrika } from "src/components/YMetrika"
|
||||||
import { GAnalytics } from "src/components/GAnalytics"
|
import { GAnalytics } from "src/components/GAnalytics"
|
||||||
|
import { Theme } from "@/ui/theme/Theme"
|
||||||
|
|
||||||
export default class MyApp extends App {
|
export default class MyApp extends App {
|
||||||
render() {
|
render() {
|
||||||
@@ -41,7 +42,9 @@ export default class MyApp extends App {
|
|||||||
<YMetrika account={24027460} />
|
<YMetrika account={24027460} />
|
||||||
<GAnalytics account={"UA-60209057-1"} />
|
<GAnalytics account={"UA-60209057-1"} />
|
||||||
</Head>
|
</Head>
|
||||||
<Component {...pageProps} />
|
<Theme>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</Theme>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import { useMobile } from "src/hooks/useMobile"
|
|||||||
import { Gosuslugi } from "../Gosuslugi"
|
import { Gosuslugi } from "../Gosuslugi"
|
||||||
import { Navigation } from "@/components/Navigation"
|
import { Navigation } from "@/components/Navigation"
|
||||||
import { Box } from "@/ui/Box"
|
import { Box } from "@/ui/Box"
|
||||||
|
import { Button } from "@/ui/Button"
|
||||||
|
import { EyeOpenIcon } from "@radix-ui/react-icons"
|
||||||
|
import { useTheme } from "@/ui/theme/useTheme"
|
||||||
|
|
||||||
export type AppProps = {
|
export type AppProps = {
|
||||||
showAuthor?: boolean
|
showAuthor?: boolean
|
||||||
@@ -25,6 +28,7 @@ export const App: React.FC<AppProps> = ({ showAuthor = false, menu, ...props })
|
|||||||
const blockStyle = {
|
const blockStyle = {
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
}
|
}
|
||||||
|
const { theme, setTheme } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigContext.Provider value={{
|
<ConfigContext.Provider value={{
|
||||||
@@ -61,6 +65,12 @@ export const App: React.FC<AppProps> = ({ showAuthor = false, menu, ...props })
|
|||||||
}}>
|
}}>
|
||||||
<Gosuslugi />
|
<Gosuslugi />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Button theme="icon" size="default" onClick={() => {
|
||||||
|
setTheme(theme === "default" ? "contrast" : "default")
|
||||||
|
}}>
|
||||||
|
<EyeOpenIcon width={24} height={24} />
|
||||||
|
</Button>
|
||||||
</Footer>
|
</Footer>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: box-shadow 150ms;
|
transition: box-shadow 150ms;
|
||||||
|
|
||||||
|
border: var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover {
|
.card:hover {
|
||||||
@@ -52,8 +54,8 @@
|
|||||||
|
|
||||||
border-radius: var(--radius) var(--radius) 0 0;
|
border-radius: var(--radius) var(--radius) 0 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: var(--radius);
|
height: var(--size-m);
|
||||||
top: calc(var(--radius) * -1);
|
top: calc(var(--size-m) * -1);
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,10 +70,9 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
--offset: var(--size-m);
|
--offset: var(--size-m);
|
||||||
--r: var(--size-s);
|
|
||||||
left: var(--offset);
|
left: var(--offset);
|
||||||
right: var(--offset);
|
right: var(--offset);
|
||||||
bottom: var(--offset);
|
bottom: var(--offset);
|
||||||
|
|
||||||
border-radius: var(--radius) var(--radius) var(--r) var(--r);
|
border-radius: var(--radius-small);
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import { Author } from "./Author"
|
|||||||
import { Spacer } from "../Spacer"
|
import { Spacer } from "../Spacer"
|
||||||
import { Contacts } from "../Contacts"
|
import { Contacts } from "../Contacts"
|
||||||
import { Box } from "@/ui/Box"
|
import { Box } from "@/ui/Box"
|
||||||
|
import { Panel } from "@/ui/Panel"
|
||||||
|
|
||||||
export type FooterProps = {
|
export type FooterProps = {
|
||||||
showAuthor: boolean
|
showAuthor: boolean
|
||||||
@@ -14,10 +15,18 @@ export type FooterProps = {
|
|||||||
|
|
||||||
export const Footer: React.FC<FooterProps> = ({ showAuthor, children }) => (
|
export const Footer: React.FC<FooterProps> = ({ showAuthor, children }) => (
|
||||||
<footer className={cx(s.footer, "opposite")}>
|
<footer className={cx(s.footer, "opposite")}>
|
||||||
<Box vertical align={false} style={{
|
<Box>
|
||||||
marginBottom: "var(--size-l)",
|
<Box vertical align={false} style={{
|
||||||
}}>
|
marginBottom: "var(--size-l)",
|
||||||
<Contacts />
|
}}>
|
||||||
|
<Contacts />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Panel style={{
|
||||||
|
marginBottom: "var(--size-l)",
|
||||||
|
}}>
|
||||||
|
<Contacts />
|
||||||
|
</Panel>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
.menuItem {
|
.menuItem {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: var(--color-back-main);
|
background-color: var(--color-back-main);
|
||||||
border-radius: var(--size-s);
|
border-radius: var(--radius-small);
|
||||||
transition: box-shadow 200ms ease-in, background-color 200ms ease-in;
|
transition: box-shadow 200ms ease-in, background-color 200ms ease-in;
|
||||||
padding: var(--size-s) var(--size-s);
|
padding: var(--size-s) var(--size-s);
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,12 @@
|
|||||||
|
|
||||||
.menuItem.active:hover {
|
.menuItem.active:hover {
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
|
color: var(--color-hover);
|
||||||
|
background-color: var(--color-hover-back);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuItem.active:hover a {
|
||||||
|
color: var(--color-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menuItem.current {
|
.menuItem.current {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { ConfigContext } from "src/context/ConfigContext"
|
|||||||
import { Box } from "@/ui/Box"
|
import { Box } from "@/ui/Box"
|
||||||
import { Spacer } from "../Spacer"
|
import { Spacer } from "../Spacer"
|
||||||
import { useToggle } from "react-use"
|
import { useToggle } from "react-use"
|
||||||
|
import { Button } from "@/ui/Button"
|
||||||
|
import { EyeOpenIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
export type NavigationProps = {
|
export type NavigationProps = {
|
||||||
style?: React.CSSProperties
|
style?: React.CSSProperties
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import s from "./wrapper.module.css"
|
import s from "./wrapper.module.css"
|
||||||
|
|
||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
|
import { useTheme } from "@/ui/theme/useTheme"
|
||||||
|
|
||||||
export type WrapperProps = {
|
export type WrapperProps = {
|
||||||
header: React.ReactNode
|
header: React.ReactNode
|
||||||
@@ -10,14 +11,18 @@ export type WrapperProps = {
|
|||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Wrapper: React.FC<WrapperProps> = props => (
|
export const Wrapper: React.FC<WrapperProps> = props => {
|
||||||
<div className={cx(s.container, "theme-default")} style={props.style}>
|
const { theme } = useTheme()
|
||||||
{props.header}
|
|
||||||
|
|
||||||
<main className={s.main} style={props.mainStyle}>
|
return (
|
||||||
{props.children}
|
<div className={cx(s.container, `theme-${theme}`)} style={props.style}>
|
||||||
</main>
|
{props.header}
|
||||||
|
|
||||||
{props.footer}
|
<main className={s.main} style={props.mainStyle}>
|
||||||
</div>
|
{props.children}
|
||||||
)
|
</main>
|
||||||
|
|
||||||
|
{props.footer}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
--color-back-opposite: hsl(0, 0%, 15%);
|
--color-back-opposite: hsl(0, 0%, 15%);
|
||||||
|
|
||||||
--color-shadow: rgba(27, 51, 88, 0.25);
|
--color-shadow: rgba(27, 51, 88, 0.25);
|
||||||
|
--color-hover: none;
|
||||||
|
|
||||||
/* SIZE */
|
/* SIZE */
|
||||||
--size-xs: 0.35rem;
|
--size-xs: 0.35rem;
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
|
|
||||||
/* EFFECTS */
|
/* EFFECTS */
|
||||||
--radius: 16px;
|
--radius: 16px;
|
||||||
|
--radius-small: 12px;
|
||||||
--shadow:
|
--shadow:
|
||||||
0 2px 4px rgba(27, 51, 88, 0.2),
|
0 2px 4px rgba(27, 51, 88, 0.2),
|
||||||
0 4px 8px -8px rgba(0, 0, 0, 0.2),
|
0 4px 8px -8px rgba(0, 0, 0, 0.2),
|
||||||
@@ -51,6 +53,7 @@
|
|||||||
--shadow-active:
|
--shadow-active:
|
||||||
0 12px 24px -8px rgba(27, 51, 88, 0.25),
|
0 12px 24px -8px rgba(27, 51, 88, 0.25),
|
||||||
0 8px 16px -8px rgba(0, 0, 0, 0.3);
|
0 8px 16px -8px rgba(0, 0, 0, 0.3);
|
||||||
|
--border: none;
|
||||||
|
|
||||||
/* SPECIAL */
|
/* SPECIAL */
|
||||||
--article-line-height: 1.5em;
|
--article-line-height: 1.5em;
|
||||||
@@ -191,3 +194,45 @@ h3 {
|
|||||||
--shadow-active: none;
|
--shadow-active: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* CONTRAST THEME */
|
||||||
|
.theme-contrast {
|
||||||
|
/* FONTS */
|
||||||
|
--title-font: 'Roboto', Helvetica, sans-serif;
|
||||||
|
--text-font: 'Roboto', Helvetica, sans-serif;
|
||||||
|
|
||||||
|
/* TEXT COLOR */
|
||||||
|
--color-text: black;
|
||||||
|
--color-text-second: black;
|
||||||
|
--color-text-opposite: white;
|
||||||
|
|
||||||
|
--color-back-main: white;
|
||||||
|
--color-back: white;
|
||||||
|
--color-back-active: hsl(240, 10%, 20%);
|
||||||
|
/* --color-back-opposite: hsl(240, 10%, 80%); */
|
||||||
|
--color-back-opposite: black;
|
||||||
|
--color-link: hsl(240, 10%, 40%);
|
||||||
|
--color-link-opposite: hsl(240, 10%, 60%);
|
||||||
|
|
||||||
|
--color-hover: white;
|
||||||
|
--color-hover-back: black;
|
||||||
|
|
||||||
|
/* EFFECTS */
|
||||||
|
/* --radius: 0px; */
|
||||||
|
--shadow: none;
|
||||||
|
--shadow-active: none;
|
||||||
|
|
||||||
|
/* FONTS */
|
||||||
|
--font-size-default: 14pt;
|
||||||
|
--font-size-accent: 24pt;
|
||||||
|
--font-size-second: 12pt;
|
||||||
|
|
||||||
|
--font-size-h1: 5rem;
|
||||||
|
--font-size-h2: 3rem;
|
||||||
|
--font-size-h3: 1.5rem;
|
||||||
|
|
||||||
|
/* OTHER */
|
||||||
|
--radius: 0;
|
||||||
|
--radius-small: 0;
|
||||||
|
--border: 5px solid var(--color-back-opposite);
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ const cx = classnames.bind(s)
|
|||||||
export type BoxProps = {
|
export type BoxProps = {
|
||||||
as?: keyof HTMLElementTagNameMap
|
as?: keyof HTMLElementTagNameMap
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
|
className?: string
|
||||||
style?: React.CSSProperties
|
style?: React.CSSProperties
|
||||||
wrap?: boolean
|
wrap?: boolean
|
||||||
align?: boolean
|
align?: boolean
|
||||||
@@ -22,10 +23,11 @@ export const Box: React.FC<BoxProps> = ({
|
|||||||
center = false,
|
center = false,
|
||||||
align = true,
|
align = true,
|
||||||
style,
|
style,
|
||||||
|
className,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const newProps = {
|
const newProps = {
|
||||||
className: cx(s.box, { vertical, align, center, wrap }),
|
className: cx(s.box, className, { vertical, align, center, wrap }),
|
||||||
style,
|
style,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import s from "./panel.module.css"
|
||||||
|
|
||||||
|
import { Box } from "../Box"
|
||||||
|
|
||||||
|
export type PanelProps = {
|
||||||
|
children?: React.ReactNode
|
||||||
|
style?: React.CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Panel: React.FC<PanelProps> = ({
|
||||||
|
style,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Box vertical align={false} className={`${s.panel} ${s.default}`} style={style}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.panel {
|
||||||
|
padding: var(--size-m);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.default {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { useState } from "react"
|
||||||
|
import { ThemeContext } from "./ThemeContext"
|
||||||
|
import { ThemeType } from "./types"
|
||||||
|
|
||||||
|
export type ThemeProps = {
|
||||||
|
children?: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Theme: React.FC<ThemeProps> = ({ children }) => {
|
||||||
|
const [theme, setTheme] = useState<ThemeType>("default")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={[theme, setTheme]}>
|
||||||
|
{children}
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { createContext } from "react"
|
||||||
|
import { SetTheme, ThemeType } from "./types"
|
||||||
|
|
||||||
|
export const ThemeContext = createContext<[ThemeType, SetTheme]>("default")
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export type ThemeType = "default" | "contrast"
|
||||||
|
export type SetTheme = (theme: ThemeType) => void
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { useContext } from "react"
|
||||||
|
import { ThemeContext } from "./ThemeContext"
|
||||||
|
import { SetTheme, ThemeType } from "./types"
|
||||||
|
|
||||||
|
export type UseThemeResult = {
|
||||||
|
theme: ThemeType
|
||||||
|
setTheme: SetTheme
|
||||||
|
// toggleTheme:
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTheme(): UseThemeResult {
|
||||||
|
const [theme, setTheme] = useContext(ThemeContext)
|
||||||
|
|
||||||
|
return { theme, setTheme }
|
||||||
|
}
|
||||||
Reference in new issue
Block a user