Merge pull request #121 from tmshv/upd/contrast

Add contrast theme
This commit is contained in:
tmshv authored and GitHub committed 2022-10-12 16:48:05 +06:00
commit 792161b40d
20 files changed
+252 -56

No files matched your search

+4 -1
View File
@@ -8,6 +8,7 @@ import App from "next/app"
import Head from "next/head"
import { YMetrika } from "src/components/YMetrika"
import { GAnalytics } from "src/components/GAnalytics"
import { Theme } from "@/ui/theme/Theme"
export default class MyApp extends App {
render() {
@@ -41,7 +42,9 @@ export default class MyApp extends App {
<YMetrika account={24027460} />
<GAnalytics account={"UA-60209057-1"} />
</Head>
<Component {...pageProps} />
<Theme>
<Component {...pageProps} />
</Theme>
</>
)
}
+1 -2
View File
@@ -7,8 +7,7 @@ export default function Document() {
{/* Google Fonts */}
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;900&display=swap"
rel="preload"
as="style"
rel="stylesheet"
/>
</Head>
+51 -4
View File
@@ -7,9 +7,22 @@ import { Breadcrumbs } from "../Breadcrumbs"
import { IBreadcumbsPart, IMenu } from "src/types"
import { useRouter } from "next/router"
import { useMobile } from "src/hooks/useMobile"
import { Gosuslugi } from "../Gosuslugi"
import { Navigation } from "@/components/Navigation"
import { Box } from "@/ui/Box"
import { Button } from "@/ui/Button"
import { EyeClosedIcon, EyeOpenIcon } from "@radix-ui/react-icons"
import { useTheme } from "@/ui/theme/useTheme"
import { Copyright } from "../Copyright"
import { Spacer } from "../Spacer"
import { Author } from "../Author"
import { Title } from "@/ui/Title"
import { Panel } from "@/ui/Panel"
import { Contacts } from "../Contacts"
import dynamic from "next/dynamic"
const Gosuslugi = dynamic(import("../Gosuslugi").then(module => module.Gosuslugi), {
ssr: false,
})
export type AppProps = {
showAuthor?: boolean
@@ -25,6 +38,7 @@ export const App: React.FC<AppProps> = ({ showAuthor = false, menu, ...props })
const blockStyle = {
justifyContent: "center",
}
const { theme, setTheme } = useTheme()
return (
<ConfigContext.Provider value={{
@@ -50,9 +64,30 @@ export const App: React.FC<AppProps> = ({ showAuthor = false, menu, ...props })
</header>
)}
footer={(
<Footer
showAuthor={showAuthor}
>
<Footer>
<Box gap={40} align={false} style={{
marginBottom: "var(--size-l)",
}}>
<Panel ghost>
<Contacts />
</Panel>
<Panel ghost>
<Title level={3}>
Версия для слабовидящих
</Title>
<Button theme="icon" size="default" onClick={() => {
setTheme(theme === "default" ? "contrast" : "default")
}}>
{theme === "default" ? (
<EyeOpenIcon width={24} height={24} />
) : (
<EyeClosedIcon width={24} height={24} />
)}
</Button>
</Panel>
</Box>
<div style={{
backgroundColor: "white",
padding: 10,
@@ -61,6 +96,18 @@ export const App: React.FC<AppProps> = ({ showAuthor = false, menu, ...props })
}}>
<Gosuslugi />
</div>
<Box>
<Copyright />
{!showAuthor ? null : (
<>
<Spacer as={"span"} />
<Author />
</>
)}
</Box>
</Footer>
)}
>
@@ -1,6 +1,6 @@
export const Author: React.FC = () => (
<span>
Разработка и поддержка сайта —
Разработка и поддержка — {" "}
<a href="http://tmshv.ru?utm_source=artshburg&utm_medium=developer&utm_campaign=tmshv_ru">
www.tmshv.ru
</a>
+5 -4
View File
@@ -5,6 +5,8 @@
box-shadow: var(--shadow);
overflow: hidden;
transition: box-shadow 150ms;
border: var(--border);
}
.card:hover {
@@ -52,8 +54,8 @@
border-radius: var(--radius) var(--radius) 0 0;
width: 100%;
height: var(--radius);
top: calc(var(--radius) * -1);
height: var(--size-m);
top: calc(var(--size-m) * -1);
left: 0;
}
@@ -68,10 +70,9 @@
position: absolute;
--offset: var(--size-m);
--r: var(--size-s);
left: var(--offset);
right: var(--offset);
bottom: var(--offset);
border-radius: var(--radius) var(--radius) var(--r) var(--r);
border-radius: var(--radius-small);
}
+2 -1
View File
@@ -3,13 +3,14 @@ import s from "./contacts.module.css"
import { useContext } from "react"
import { ConfigContext } from "src/context/ConfigContext"
import Link from "next/link"
import { Title } from "@/ui/Title"
export const Contacts: React.FC = props => {
const { contacts } = useContext(ConfigContext)
return (
<>
<strong className={s.strong}>Контактная информация</strong>
<Title level={3}>Контактная информация</Title>
{contacts.map((x, i) => (
<p key={i} className={s.link}>
-1
View File
@@ -7,4 +7,3 @@
border-top-left-radius: var(--radius);
border-top-right-radius: var(--radius);
}
+3 -28
View File
@@ -1,37 +1,12 @@
import s from "./footer.module.css"
import cx from "classnames"
import { Copyright } from "../Copyright"
import { Author } from "./Author"
import { Spacer } from "../Spacer"
import { Contacts } from "../Contacts"
import { Box } from "@/ui/Box"
export type FooterProps = {
showAuthor: boolean
children?: React.ReactNode
}
export const Footer: React.FC<FooterProps> = ({ showAuthor, children }) => (
<footer className={cx(s.footer, "opposite")}>
<Box vertical align={false} style={{
marginBottom: "var(--size-l)",
}}>
<Contacts />
</Box>
export const Footer: React.FC<FooterProps> = ({ children }) => (
<footer className={`${s.footer} opposite`}>
{children}
<Box>
<Copyright />
{!showAuthor ? null : (
<>
<Spacer as={"span"} />
<Author />
</>
)}
</Box>
</footer >
</footer>
)
+7 -1
View File
@@ -21,7 +21,7 @@
.menuItem {
user-select: none;
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;
padding: var(--size-s) var(--size-s);
}
@@ -32,6 +32,12 @@
.menuItem.active:hover {
box-shadow: var(--shadow);
color: var(--color-hover);
background-color: var(--color-hover-back);
}
.menuItem.active:hover a {
color: var(--color-hover);
}
.menuItem.current {
+2
View File
@@ -7,6 +7,8 @@ import { ConfigContext } from "src/context/ConfigContext"
import { Box } from "@/ui/Box"
import { Spacer } from "../Spacer"
import { useToggle } from "react-use"
import { Button } from "@/ui/Button"
import { EyeOpenIcon } from "@radix-ui/react-icons"
export type NavigationProps = {
style?: React.CSSProperties
+14 -9
View File
@@ -1,6 +1,7 @@
import s from "./wrapper.module.css"
import cx from "classnames"
import { useTheme } from "@/ui/theme/useTheme"
export type WrapperProps = {
header: React.ReactNode
@@ -10,14 +11,18 @@ export type WrapperProps = {
children?: React.ReactNode
}
export const Wrapper: React.FC<WrapperProps> = props => (
<div className={cx(s.container, "theme-default")} style={props.style}>
{props.header}
export const Wrapper: React.FC<WrapperProps> = props => {
const { theme } = useTheme()
<main className={s.main} style={props.mainStyle}>
{props.children}
</main>
return (
<div className={cx(s.container, `theme-${theme}`)} style={props.style}>
{props.header}
{props.footer}
</div>
)
<main className={s.main} style={props.mainStyle}>
{props.children}
</main>
{props.footer}
</div>
)
}
+68 -2
View File
@@ -34,6 +34,7 @@
--color-back-opposite: hsl(0, 0%, 15%);
--color-shadow: rgba(27, 51, 88, 0.25);
--color-hover: none;
/* SIZE */
--size-xs: 0.35rem;
@@ -44,6 +45,7 @@
/* EFFECTS */
--radius: 16px;
--radius-small: 12px;
--shadow:
0 2px 4px rgba(27, 51, 88, 0.2),
0 4px 8px -8px rgba(0, 0, 0, 0.2),
@@ -51,6 +53,7 @@
--shadow-active:
0 12px 24px -8px rgba(27, 51, 88, 0.25),
0 8px 16px -8px rgba(0, 0, 0, 0.3);
--border: none;
/* SPECIAL */
--article-line-height: 1.5em;
@@ -86,6 +89,7 @@ body {
font-family: var(--text-font);
}
/* TITLE */
h1 {
font-family: var(--title-font);
text-align: center;
@@ -103,8 +107,7 @@ h2 {
}
h3 {
margin: var(--size-m) 0;
margin-bottom: var(--size-s);
font-size: var(--font-size-h3);
hyphens: none;
@@ -191,3 +194,66 @@ h3 {
--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: 16pt;
--font-size-accent: 24pt;
--font-size-second: 14pt;
--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);
}
/* CONTRAST + DARK THEME */
@media (prefers-color-scheme: dark) {
.theme-contrast {
/* TEXT COLOR */
--color-text: white;
--color-text-second: white;
--color-text-opposite: black;
--color-back-main: black;
--color-back: black;
--color-back-opposite: white;
/* --color-back-active: hsl(240, 10%, 20%); */
/* --color-link: hsl(240, 10%, 40%); */
/* --color-link-opposite: hsl(240, 10%, 60%); */
--color-hover: white;
--color-hover-back: white;
}
}
+9 -2
View File
@@ -8,11 +8,13 @@ const cx = classnames.bind(s)
export type BoxProps = {
as?: keyof HTMLElementTagNameMap
children?: React.ReactNode
className?: string
style?: React.CSSProperties
wrap?: boolean
align?: boolean
vertical?: boolean
center?: boolean
gap?: number
}
export const Box: React.FC<BoxProps> = ({
@@ -22,11 +24,16 @@ export const Box: React.FC<BoxProps> = ({
center = false,
align = true,
style,
gap,
className,
...props
}) => {
const newProps = {
className: cx(s.box, { vertical, align, center, wrap }),
style,
className: cx(s.box, className, { vertical, align, center, wrap }),
style: {
...style,
gap,
},
}
return createElement(as, newProps, props.children)
+23
View File
@@ -0,0 +1,23 @@
import s from "./panel.module.css"
import { Box } from "../Box"
export type PanelProps = {
children?: React.ReactNode
style?: React.CSSProperties
ghost?: boolean
}
export const Panel: React.FC<PanelProps> = ({
style,
children,
ghost = false,
}) => {
const variant = ghost ? s.ghost : s.default
return (
<Box vertical align={false} className={`${s.panel} ${variant}`} style={style}>
{children}
</Box>
)
}
+12
View File
@@ -0,0 +1,12 @@
.panel {
border-radius: var(--radius);
}
.default {
padding: var(--size-m);
background-color: white;
}
.ghost {
background-color: none;
}
+12
View File
@@ -0,0 +1,12 @@
import { createElement } from "react"
export type TitleProps = {
level?: 1 | 2 | 3 | 4 | 5 | 6
children?: React.ReactNode
className?: string
style?: React.CSSProperties
}
export const Title: React.FC<TitleProps> = ({ level = 1, children, ...props }) => {
return createElement(`h${level}`, props, children)
}
+17
View File
@@ -0,0 +1,17 @@
import { useLocalStorage } from "react-use"
import { ThemeContext } from "./ThemeContext"
import { ThemeType } from "./types"
export type ThemeProps = {
children?: React.ReactNode
}
export const Theme: React.FC<ThemeProps> = ({ children }) => {
const [theme, setTheme] = useLocalStorage<ThemeType>("hudozka-theme", "default")
return (
<ThemeContext.Provider value={[theme ?? "default", setTheme]}>
{children}
</ThemeContext.Provider>
)
}
+4
View File
@@ -0,0 +1,4 @@
import { createContext } from "react"
import { SetTheme, ThemeType } from "./types"
export const ThemeContext = createContext<[ThemeType, SetTheme]>(["default", () => {}])
+2
View File
@@ -0,0 +1,2 @@
export type ThemeType = "default" | "contrast"
export type SetTheme = (theme: ThemeType) => void
+15
View File
@@ -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 }
}