-20 commits

This commit is contained in:
Untone 2024-10-07 00:44:40 +03:00
parent c789124ff7
commit 504b139df5
20 changed files with 444 additions and 350 deletions

View File

@ -671,3 +671,7 @@ a[data-toggle='tooltip'] {
} }
} }
} }
.figureAlignColumn {
clear: both;
}

View File

@ -4,6 +4,7 @@ import { A, useSearchParams } from '@solidjs/router'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js' import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js'
import { isServer } from 'solid-js/web' import { isServer } from 'solid-js/web'
import { useFeed } from '~/context/feed' import { useFeed } from '~/context/feed'
import { useLocalize } from '~/context/localize' import { useLocalize } from '~/context/localize'
import { useReactions } from '~/context/reactions' import { useReactions } from '~/context/reactions'
@ -19,7 +20,6 @@ import { capitalize } from '~/utils/capitalize'
import { AuthorBadge } from '../Author/AuthorBadge' import { AuthorBadge } from '../Author/AuthorBadge'
import { CardTopic } from '../Feed/CardTopic' import { CardTopic } from '../Feed/CardTopic'
import { FeedArticlePopup } from '../Feed/FeedArticlePopup' import { FeedArticlePopup } from '../Feed/FeedArticlePopup'
import stylesHeader from '../HeaderNav/Header.module.scss'
import { Icon } from '../_shared/Icon' import { Icon } from '../_shared/Icon'
import { Image } from '../_shared/Image' import { Image } from '../_shared/Image'
import { InviteMembers } from '../_shared/InviteMembers' import { InviteMembers } from '../_shared/InviteMembers'
@ -30,13 +30,15 @@ import { ShareModal } from '../_shared/ShareModal'
import { ImageSwiper } from '../_shared/SolidSwiper' import { ImageSwiper } from '../_shared/SolidSwiper'
import { TableOfContents } from '../_shared/TableOfContents' import { TableOfContents } from '../_shared/TableOfContents'
import { VideoPlayer } from '../_shared/VideoPlayer' import { VideoPlayer } from '../_shared/VideoPlayer'
import styles from './Article.module.scss'
import { AudioHeader } from './AudioHeader' import { AudioHeader } from './AudioHeader'
import { AudioPlayer } from './AudioPlayer' import { AudioPlayer } from './AudioPlayer'
import { CommentsTree } from './CommentsTree' import { CommentsTree } from './CommentsTree'
import { SharePopup, getShareUrl } from './SharePopup' import { SharePopup, getShareUrl } from './SharePopup'
import { ShoutRatingControl } from './ShoutRatingControl' import { ShoutRatingControl } from './ShoutRatingControl'
import stylesHeader from '../HeaderNav/Header.module.scss'
import styles from './Article.module.scss'
type Props = { type Props = {
article: Shout article: Shout
} }
@ -366,7 +368,7 @@ export const FullArticle = (props: Props) => {
props.article.layout !== 'image' props.article.layout !== 'image'
} }
> >
<figure class="img-align-column"> <figure class={styles.figureAlignColumn}>
<Image <Image
width={800} width={800}
alt={props.article.cover_caption || ''} alt={props.article.cover_caption || ''}

View File

@ -133,7 +133,7 @@
} }
.authControl { .authControl {
color: $link-color; color: $color-primary;
margin-top: 1em; margin-top: 1em;
text-align: center; text-align: center;
} }

View File

@ -10,10 +10,10 @@
a:visited { a:visited {
background: transparent; background: transparent;
border: none; border: none;
color: $link-color; color: $color-primary;
&:hover { &:hover {
background: $link-color; background: $color-primary;
color: #fff !important; color: #fff !important;
} }
} }

View File

@ -1,16 +1,16 @@
import type { Shout } from '~/graphql/schema/core.gen'
import { For } from 'solid-js' import { For } from 'solid-js'
import type { Shout } from '~/graphql/schema/core.gen'
import { ArticleCard } from './ArticleCard' import { ArticleCard } from './ArticleCard'
import styles from '~/styles/views/Feed.module.scss'
export default (props: { articles: Shout[] }) => ( export default (props: { articles: Shout[] }) => (
<div class="floor floor--7"> <div class="floor floor--7">
<div class="wide-container"> <div class="wide-container">
<div class="short-cards"> <div class={styles['short-cards']}>
<For each={props.articles}> <For each={props.articles}>
{(a) => ( {(a) => (
<div class="short-card"> <div class={styles['short-card']}>
<ArticleCard <ArticleCard
article={a} article={a}
settings={{ settings={{

View File

@ -86,7 +86,7 @@
vertical-align: middle; vertical-align: middle;
width: 100px; width: 100px;
[data-editor-dark-mode='true'] & { [data-theme='dark'] & {
filter: invert(1); filter: invert(1);
} }
} }

View File

@ -2,7 +2,8 @@ import { For, Show, createResource, createSignal, onCleanup } from 'solid-js'
import { debounce } from 'throttle-debounce' import { debounce } from 'throttle-debounce'
import { Button } from '~/components/_shared/Button' import { Button } from '~/components/_shared/Button'
import { Icon } from '~/components/_shared/Icon' import { Icon } from '~/components/_shared/Icon'
import { useFeed } from '~/context/feed' import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
import { SHOUTS_PER_PAGE, useFeed } from '~/context/feed'
import { useLocalize } from '~/context/localize' import { useLocalize } from '~/context/localize'
import type { Shout } from '~/graphql/schema/core.gen' import type { Shout } from '~/graphql/schema/core.gen'
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll' import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
@ -13,9 +14,7 @@ import { SearchResultItem } from './SearchResultItem'
import styles from './SearchModal.module.scss' import styles from './SearchModal.module.scss'
// @@TODO handle empty article options after backend support (subtitle, cover, etc.) // @@TODO handle empty article options after backend support (subtitle, cover, etc.)
// @@TODO implement load more
// @@TODO implement FILTERS & TOPICS // @@TODO implement FILTERS & TOPICS
// @@TODO use save/restoreScrollPosition if needed
const getSearchCoincidences = ({ str, intersection }: { str: string; intersection: string }) => const getSearchCoincidences = ({ str, intersection }: { str: string; intersection: string }) =>
`<span>${str.replaceAll( `<span>${str.replaceAll(
@ -48,34 +47,30 @@ export const SearchModal = () => {
const [inputValue, setInputValue] = createSignal('') const [inputValue, setInputValue] = createSignal('')
const [isLoading, setIsLoading] = createSignal(false) const [isLoading, setIsLoading] = createSignal(false)
const [offset, setOffset] = createSignal<number>(0) const [offset, setOffset] = createSignal<number>(0)
const fetchSearchResults = async () => {
setIsLoading(true)
saveScrollPosition()
const { hasMore, newShouts } = await loadShoutsSearch({
limit: FEED_PAGE_SIZE,
text: inputValue(),
offset: offset()
})
setIsLoading(false)
setOffset(newShouts.length)
setIsLoadMoreButtonVisible(hasMore)
return newShouts
}
const [searchResultsList, { refetch: loadSearchResults, mutate: setSearchResultsList }] = createResource< const [searchResultsList, { refetch: loadSearchResults, mutate: setSearchResultsList }] = createResource<
Shout[] Shout[]
>( >(fetchSearchResults, { ssrLoadFrom: 'initial', initialValue: [] })
async () => {
setIsLoading(true)
saveScrollPosition()
const { hasMore, newShouts } = await loadShoutsSearch({
limit: FEED_PAGE_SIZE,
text: inputValue(),
offset: offset()
})
setIsLoading(false)
setOffset(newShouts.length)
setIsLoadMoreButtonVisible(hasMore)
return newShouts
},
{
ssrLoadFrom: 'initial',
initialValue: []
}
)
let searchEl: HTMLInputElement const [searchEl, setSearchEl] = createSignal<HTMLInputElement | undefined>()
const debouncedLoadMore = debounce(500, loadSearchResults) const debouncedLoadMore = debounce(500, loadSearchResults)
const handleQueryInput = async () => { const handleQueryInput = async () => {
setInputValue(searchEl.value) setInputValue(searchEl()?.value ?? '')
if (searchEl.value?.length > 2) { if ((searchEl()?.value?.length || 0) > 2) {
await debouncedLoadMore() await debouncedLoadMore()
} else { } else {
setIsLoading(false) setIsLoading(false)
@ -101,6 +96,11 @@ export const SearchModal = () => {
// console.debug('[SearchModal] cleanup debouncing search') // console.debug('[SearchModal] cleanup debouncing search')
}) })
const loadMoreResults = async () => {
const result = await fetchSearchResults()
return result as LoadMoreItems
}
return ( return (
<div class={styles.searchContainer}> <div class={styles.searchContainer}>
<input <input
@ -109,7 +109,7 @@ export const SearchModal = () => {
class={styles.searchInput} class={styles.searchInput}
onInput={handleQueryInput} onInput={handleQueryInput}
onKeyDown={enterQuery} onKeyDown={enterQuery}
ref={(el: HTMLInputElement) => (searchEl = el)} ref={setSearchEl}
/> />
<Button <Button
@ -127,28 +127,26 @@ export const SearchModal = () => {
<Show when={!isLoading()}> <Show when={!isLoading()}>
<Show when={searchResultsList()}> <Show when={searchResultsList()}>
<For each={prepareSearchResults(searchResultsList(), inputValue())}> <LoadMoreWrapper
{(article: Shout) => ( loadFunction={loadMoreResults}
<div> pageSize={SHOUTS_PER_PAGE}
<SearchResultItem hidden={!isLoadMoreButtonVisible()}
article={article} >
settings={{ <For each={prepareSearchResults(searchResultsList(), inputValue())}>
isFloorImportant: true, {(article: Shout) => (
isSingle: true, <div>
nodate: true <SearchResultItem
}} article={article}
/> settings={{
</div> isFloorImportant: true,
)} isSingle: true,
</For> nodate: true
}}
<Show when={isLoadMoreButtonVisible()}> />
<p class="load-more-container"> </div>
<button class="button" onClick={loadSearchResults}> )}
{t('Load more')} </For>
</button> </LoadMoreWrapper>
</p>
</Show>
</Show> </Show>
<Show when={Array.isArray(searchResultsList()) && searchResultsList().length === 0}> <Show when={Array.isArray(searchResultsList()) && searchResultsList().length === 0}>

View File

@ -3,11 +3,12 @@ import { For, Show, createSignal, onMount } from 'solid-js'
import { useSearchParams } from '@solidjs/router' import { useSearchParams } from '@solidjs/router'
import { useFeed } from '~/context/feed' import { useFeed } from '~/context/feed'
import { useLocalize } from '~/context/localize' import { useLocalize } from '~/context/localize'
import type { SearchResult } from '~/graphql/schema/core.gen' import type { SearchResult, Shout } from '~/graphql/schema/core.gen'
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll' import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
import { ArticleCard } from '../Feed/ArticleCard' import { ArticleCard } from '../Feed/ArticleCard'
import '~/styles/views/Search.module.scss' import '~/styles/views/Search.module.scss'
import { LoadMoreItems, LoadMoreWrapper } from '../_shared/LoadMoreWrapper'
type Props = { type Props = {
query: string query: string
@ -31,19 +32,22 @@ export const SearchView = (props: Props) => {
const loadMore = async () => { const loadMore = async () => {
saveScrollPosition() saveScrollPosition()
let results: Shout[] = []
if (query()) { if (query()) {
console.log(query()) console.log(query())
const { hasMore } = await loadShoutsSearch({ const { hasMore, newShouts } = await loadShoutsSearch({
text: query(), text: query(),
offset: offset(), offset: offset(),
limit: LOAD_MORE_PAGE_SIZE limit: LOAD_MORE_PAGE_SIZE
}) })
setIsLoadMoreButtonVisible(hasMore) setIsLoadMoreButtonVisible(hasMore)
setOffset(offset() + LOAD_MORE_PAGE_SIZE) setOffset(offset() + LOAD_MORE_PAGE_SIZE)
results = newShouts
} else { } else {
console.warn('[SaerchView] no query found') console.warn('[SaerchView] no query found')
} }
restoreScrollPosition() restoreScrollPosition()
return results as LoadMoreItems
} }
onMount(() => { onMount(() => {
@ -95,13 +99,19 @@ export const SearchView = (props: Props) => {
<div class="floor"> <div class="floor">
<div class="row"> <div class="row">
<For each={sortedFeed()}> <LoadMoreWrapper
{(article) => ( pageSize={LOAD_MORE_PAGE_SIZE}
<div class="col-md-6"> hidden={!isLoadMoreButtonVisible()}
<ArticleCard article={article} desktopCoverSize="L" /> loadFunction={loadMore}
</div> >
)} <For each={sortedFeed()}>
</For> {(article) => (
<div class="col-md-6">
<ArticleCard article={article} desktopCoverSize="L" />
</div>
)}
</For>
</LoadMoreWrapper>
<Show when={isLoadMoreButtonVisible()}> <Show when={isLoadMoreButtonVisible()}>
<div class="col-md-6"> <div class="col-md-6">

View File

@ -199,9 +199,9 @@ export const TopicView = (props: Props) => {
</ul> </ul>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<div class="mode-switcher"> <div class={styles.modeSwitcher}>
{`${t('Show')} `} {`${t('Show')} `}
<span class="mode-switcher__control">{t('All posts')}</span> <span class={styles.modeSwitcherControl}>{t('All posts')}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -12,45 +12,38 @@ type Props = {
export const DarkModeToggle = (props: Props) => { export const DarkModeToggle = (props: Props) => {
const { t } = useLocalize() const { t } = useLocalize()
const [editorDarkMode, setEditorDarkMode] = createSignal(false) const [isDark, setIsDark] = createSignal(false)
onMount(() => { onMount(() => {
const editorDarkModeSelected = localStorage?.getItem('editorDarkMode') const theme = localStorage?.getItem('theme') || document.documentElement.getAttribute('theme')
const editorDarkModeAttr = document.documentElement.getAttribute('editorDarkMode') if (theme === 'dark') {
if (editorDarkModeSelected === 'true') { setIsDark(true)
setEditorDarkMode(true) document.documentElement.dataset.theme = 'dark'
document.documentElement.dataset.editorDarkMode = 'true' } else if (theme !== 'dark') {
} else if (editorDarkModeSelected === 'false') { setIsDark(false)
setEditorDarkMode(false) document.documentElement.dataset.theme = 'light'
document.documentElement.dataset.editorDarkMode = 'false'
} }
if (!(editorDarkModeAttr || editorDarkModeSelected)) { if (!theme) {
localStorage?.setItem('editorDarkMode', 'false') localStorage?.setItem('theme', 'light')
document.documentElement.dataset.editorDarkMode = 'false' document.documentElement.dataset.theme = 'light'
} }
onCleanup(() => { onCleanup(() => {
setEditorDarkMode(false) setIsDark(false)
document.documentElement.dataset.editorDarkMode = undefined document.documentElement.dataset.theme = undefined
}) })
}) })
const handleSwitchTheme = () => { const handleSwitchTheme = () => {
setEditorDarkMode(!editorDarkMode()) setIsDark(!isDark())
localStorage?.setItem('editorDarkMode', editorDarkMode() ? 'true' : 'false') localStorage?.setItem('theme', isDark() ? 'dark' : 'light')
document.documentElement.dataset.editorDarkMode = editorDarkMode() ? 'true' : 'false' document.documentElement.dataset.theme = isDark() ? 'dark' : 'light'
} }
return ( return (
<div class={clsx(styles.DarkModeToggle, props.class)}> <div class={clsx(styles.DarkModeToggle, props.class)}>
<input <input type="checkbox" id="theme-switcher" value="1" checked={isDark()} onClick={handleSwitchTheme} />
type="checkbox"
id="theme-switcher"
value="1"
checked={editorDarkMode()}
onClick={handleSwitchTheme}
/>
<label for="theme-switcher"> <label for="theme-switcher">
{t('Night mode')} {t('Night mode')}
<div class={styles.switcher}> <div class={styles.switcher}>

View File

@ -5,6 +5,7 @@ import { Show, createSignal, onMount } from 'solid-js'
import { useLocalize } from '~/context/localize' import { useLocalize } from '~/context/localize'
import { Button } from '../Button' import { Button } from '../Button'
import './cropper.css'
import styles from './ImageCropper.module.scss' import styles from './ImageCropper.module.scss'
interface CropperProps { interface CropperProps {

View File

@ -0,0 +1,28 @@
.cropper-modal {
background-color: #000 !important;
opacity: 0.4 !important;
}
.cropper-canvas {
filter: blur(2px);
}
.cropper-view-box,
.cropper-crop-box,
.cropper-line,
.cropper-point {
box-shadow: none !important;
outline: none !important;
border: none !important;
background-color: transparent !important;
}
.cropper-crop-box {
border: 2px solid #000 !important;
border-radius: 8px;
}
.cropper-view-box,
.cropper-face {
border-radius: 50%;
}

View File

@ -23,33 +23,18 @@ This grid system is a lightweight alternative to Bootstrap's grid, providing ess
## Mixins ## Mixins
### `media-breakpoint-up($breakpoint)` - `media-breakpoint-{up|down}($breakpoint)` - Applies styles at a minimum or maximum width of the specified breakpoint
- `media-breakpoint-between($lower, $upper)` - Applies styles between two breakpoints.
Applies styles at a minimum width of the specified breakpoint. ### Justify Content
- `.justify-content-{breakpoint}-{start|end|center|between|around|evenly}`
### `media-breakpoint-down($breakpoint)` ### Display
- `.d-{breakpoint}-{none|inline|inline-block|block|table|table-row|table-cell|flex|inline-flex}`
- `.d-print-{none|inline|inline-block|block|table|table-row|table-cell|flex|inline-flex}`
Applies styles at a maximum width of the specified breakpoint. `{breakpoint}` can be `xs`, `sm`, `md`, `lg`, `xl` or `xxl`.
### `media-breakpoint-between($lower, $upper)`
Applies styles between two breakpoints.
### `make-container($max-widths, $gutter)`
Creates a container with specified maximum widths and gutter.
### `make-row($gutter)`
Creates a flexbox row with specified gutter.
### `make-col($size, $columns)`
Defines a column with a specific size and total number of columns.
### `make-col-offset($size, $columns)`
Offsets a column by a specific size.
### `row-cols($count)` ### `row-cols($count)`
@ -63,3 +48,5 @@ You can customize the grid system by modifying the variables in `_globals.scss`:
- **`$grid-gutter-width`**: Width of the gutter between columns. - **`$grid-gutter-width`**: Width of the gutter between columns.
- **`$grid-breakpoints`**: Map of breakpoints for responsive design. - **`$grid-breakpoints`**: Map of breakpoints for responsive design.
- **`$container-max-widths`**: Maximum widths for containers at each breakpoint. - **`$container-max-widths`**: Maximum widths for containers at each breakpoint.
- **`$container-padding-x`**: Padding for containers.
- **`$prefix`**: Prefix for classes.

View File

@ -1,3 +1,8 @@
@import 'vars';
// Определяем $displays в начале файла
$displays: none, inline, inline-block, block, table, table-row, table-cell, flex, inline-flex;
// Миксин для media-breakpoint-up // Миксин для media-breakpoint-up
@mixin media-breakpoint-up($breakpoint, $breakpoints: $grid-breakpoints) { @mixin media-breakpoint-up($breakpoint, $breakpoints: $grid-breakpoints) {
$min-width: map-get($breakpoints, $breakpoint); $min-width: map-get($breakpoints, $breakpoint);
@ -41,14 +46,16 @@
// Миксин make-container // Миксин make-container
@mixin make-container($max-widths: $container-max-widths, $gutter: $grid-gutter-width) { @mixin make-container($max-widths: $container-max-widths, $gutter: $grid-gutter-width) {
width: 100%; width: 100%;
padding-right: $gutter; padding-right: $container-padding-x;
padding-left: $gutter; padding-left: $container-padding-x;
margin-right: auto; margin-right: auto;
margin-left: auto; margin-left: auto;
@each $breakpoint, $max-width in $max-widths { @each $breakpoint, $max-width in $max-widths {
@include media-breakpoint-up($breakpoint, $grid-breakpoints) { @include media-breakpoint-up($breakpoint, $grid-breakpoints) {
max-width: #{$max-width}; @if $max-width {
max-width: $max-width;
}
} }
} }
} }
@ -97,12 +104,12 @@
@mixin make-col-offset($size, $columns: $grid-columns) { @mixin make-col-offset($size, $columns: $grid-columns) {
$num: calc($size / $columns); $num: calc($size / $columns);
margin-left: if($num == 0, 0, calc(100% * #{$num})); margin-left: if($num ==0, 0, calc(100% * #{$num}));
} }
// Миксин row-cols // Миксин row-cols
@mixin row-cols($count) { @mixin row-cols($count) {
> * { >* {
flex: 0 0 auto; flex: 0 0 auto;
width: 100% / $count; width: 100% / $count;
} }
@ -110,16 +117,21 @@
// Миксин make-grid-columns // Миксин make-grid-columns
@mixin make-grid-columns($columns: $grid-columns, $breakpoints: $grid-breakpoints) { @mixin make-grid-columns($columns: $grid-columns, $breakpoints: $grid-breakpoints) {
@each $breakpoint, $value in $breakpoints { @each $breakpoint in map-keys($breakpoints) {
$infix: if($breakpoint == 'xs', '', "-#{$breakpoint}"); $infix: if($breakpoint =='xs', '', "-#{$breakpoint}");
@include media-breakpoint-up($breakpoint, $breakpoints) { @include media-breakpoint-up($breakpoint, $breakpoints) {
// Добавляем класс col-auto
.#{$prefix}col#{$infix}-auto {
@include make-col-auto;
}
@for $i from 1 through $columns { @for $i from 1 through $columns {
.col#{$infix}-#{$i} { .#{$prefix}col#{$infix}-#{$i} {
@include make-col($i, $columns); @include make-col($i, $columns);
} }
.offset#{$infix}-#{$i} { .#{$prefix}offset#{$infix}-#{$i} {
@include make-col-offset($i, $columns); @include make-col-offset($i, $columns);
} }
} }
@ -127,6 +139,100 @@
} }
} }
// Обновляем миксин make-justify-content
@mixin make-justify-content($breakpoints: $grid-breakpoints) {
@each $breakpoint in map-keys($breakpoints) {
@include media-breakpoint-up($breakpoint, $breakpoints) {
$infix: if($breakpoint =='xs', "", "-#{$breakpoint}");
.#{$prefix}justify-content#{$infix}-start {
justify-content: flex-start !important;
}
.#{$prefix}justify-content#{$infix}-end {
justify-content: flex-end !important;
}
.#{$prefix}justify-content#{$infix}-center {
justify-content: center !important;
}
.#{$prefix}justify-content#{$infix}-between {
justify-content: space-between !important;
}
.#{$prefix}justify-content#{$infix}-around {
justify-content: space-around !important;
}
.#{$prefix}justify-content#{$infix}-evenly {
justify-content: space-evenly !important;
}
}
}
}
// Обновляем миксин make-display-classes
@mixin make-display-classes($breakpoints: $grid-breakpoints) {
@each $breakpoint in map-keys($breakpoints) {
@include media-breakpoint-up($breakpoint, $breakpoints) {
$infix: if($breakpoint =='xs', "", "-#{$breakpoint}");
@each $value in $displays {
.#{$prefix}d#{$infix}-#{$value} {
display: $value !important;
}
}
}
}
}
// Дополнительный миксин для классов d-print-*
@mixin make-print-display-classes() {
@media print {
@each $value in $displays {
.#{$prefix}d-print-#{$value} {
display: $value !important;
}
}
}
}
// Добавляем новый миксин для генерации классов порядка
@mixin make-order-classes($breakpoints: $grid-breakpoints) {
@each $breakpoint in map-keys($breakpoints) {
@include media-breakpoint-up($breakpoint, $breakpoints) {
$infix: if($breakpoint =='xs', "", "-#{$breakpoint}");
.order#{$infix}-first {
order: -1 !important;
}
.order#{$infix}-last {
order: 9999 !important;
}
.order#{$infix}-0 {
order: 0 !important;
}
@for $i from 1 through 5 {
.order#{$infix}-#{$i} {
order: $i !important;
}
}
}
}
}
// Добавляем вызов нового миксина в конец файла
@include make-grid-columns($grid-columns, $grid-breakpoints);
@include make-display-classes;
@include make-print-display-classes;
@include make-justify-content;
@include make-order-classes;
// Генерация классов контейнера и ряда // Генерация классов контейнера и ряда
.container, .container,
.container-fluid { .container-fluid {
@ -141,5 +247,29 @@
} }
} }
// Генерация классов столбцов и смещений .container {
@include make-grid-columns($grid-columns, $grid-breakpoints); @include media-breakpoint-down(sm) {
// padding: 0 $container-padding-x * 0.5;
}
max-width: 1500px;
width: auto;
}
.row {
@include media-breakpoint-down(md) {
> * {
margin-bottom: 2.4rem;
}
}
@include media-breakpoint-down(sm) {
margin-left: divide(-$container-padding-x, 2);
margin-right: divide(-$container-padding-x, 2);
> * {
padding-left: divide($container-padding-x, 2);
padding-right: divide($container-padding-x, 2);
}
}
}

View File

@ -1,2 +1,3 @@
@import 'vars'; @import 'vars';
@import 'theme';
@import 'grid'; @import 'grid';

56
src/styles/_theme.scss Normal file
View File

@ -0,0 +1,56 @@
@import 'vars';
:root {
--background-color: #fff;
--default-color: #{$default-color};
--background-color-invert: #000;
--default-color-invert: #fff;
--link-color: #000;
--link-hover-color: #fff;
--link-hover-background: #000;
--secondary-color: #85878a;
--placeholder-color: #9fa1a7;
--placeholder-color-semi: rgb(159 169 167 / 20%);
--danger-color: $color-danger;
--lightgray-color: rgb(84 16 17 / 6%);
--selection-background: #000;
--selection-color: #fff;
--icon-filter: invert(0);
--icon-filter-hover: invert(1);
--editor-bubble-menu-background: #fff;
--blue-link: $link-color;
// names from figma
--black-50: #f7f7f8;
--black-100: #e9e9ee;
--black-300: #9fa1a7;
--black-400: #696969;
--black-500: #141414;
--white-500: #fff;
--blue-500: #2638d9;
--yellow-50: #fffbeb;
--gray-100: #f3f4f6;
}
[data-theme='dark'] {
--background-color: #121416;
--default-color: #fff;
--background-color-invert: #fff;
--default-color-invert: #121416;
--link-color: #fff;
--link-hover-color: #000;
--link-hover-background: #fff;
--selection-background: #eee;
--selection-color: #000;
--icon-filter: invert(1);
--icon-filter-hover: invert(0);
--editor-bubble-menu-background: #444;
// names from figma
--black-50: #080807;
--black-100: #161611;
--black-300: #696969;
--black-400: #969696;
--black-500: #ebebeb;
--white-500: #000;
}

View File

@ -12,11 +12,7 @@ $grid-breakpoints: (
xl: 1200px, xl: 1200px,
xxl: 1400px, xxl: 1400px,
) !default; ) !default;
$default-color: #141414;
$link-color: #2638d9;
$container-padding-x: $grid-gutter-width * 0.5 !default; $container-padding-x: $grid-gutter-width * 0.5 !default;
// Additional variables needed
$container-max-widths: $grid-breakpoints; $container-max-widths: $grid-breakpoints;
$gutters: ( $gutters: (
0: 0, 0: 0,
@ -28,3 +24,18 @@ $gutters: (
) !default; ) !default;
$grid-row-columns: 6 !default; $grid-row-columns: 6 !default;
$prefix: '' !default; $prefix: '' !default;
// Шрифты
$font-family: muller, arial, helvetica, sans-serif;
$font-size-base: 2rem;
$line-height-base: 1.6;
// Базовые цвета (не зависящие от темы)
$color-primary: #2638d9;
$color-danger: #d00820;
$default-color: #141414;
// Другие переменные
$border-radius: 2px;
$transition-base: all 0.2s ease-in-out;
$container-padding-x: $grid-gutter-width * 0.5;

View File

@ -1,60 +1,7 @@
@import 'fonts'; @import 'fonts';
@import 'vars';
:root { @import 'theme';
--background-color: #fff; @import 'grid';
--default-color: #121416;
--background-color-invert: #000;
--default-color-invert: #fff;
--link-color: #000;
--link-hover-color: #fff;
--link-hover-background: #000;
--secondary-color: #85878a;
--placeholder-color: #9fa1a7;
--placeholder-color-semi: rgb(159 169 167 / 20%);
--danger-color: #d00820;
--lightgray-color: rgb(84 16 17 / 6%);
--font: -apple-system, blinkmacsystemfont, 'Segoe UI', roboto, oxygen, ubuntu, cantarell, 'Open Sans',
'Helvetica Neue', sans-serif;
--selection-background: #000;
--selection-color: #fff;
--icon-filter: invert(0);
--icon-filter-hover: invert(1);
--editor-bubble-menu-background: #fff;
--blue-link: #2638d9;
// names from figma
--black-50: #f7f7f8;
--black-100: #e9e9ee;
--black-300: #9fa1a7;
--black-500: #141414;
--black-400: #696969;
--white-500: #fff;
--blue-500: #2638d9;
--yellow-50: #fffbeb;
--gray-100: #f3f4f6;
}
[data-editor-dark-mode='true'] {
--background-color: #121416;
--default-color: #fff;
--background-color-invert: #fff;
--default-color-invert: #121416;
--link-color: #fff;
--link-hover-color: #000;
--link-hover-background: #fff;
--selection-background: #eee;
--selection-color: #000;
--icon-filter: invert(1);
--icon-filter-hover: invert(0);
--editor-bubble-menu-background: #444;
// names from figma
--black-50: #080807;
--black-100: #161611;
--black-500: #ebebeb;
--black-400: #969696;
--white-500: #000;
}
* { * {
box-sizing: border-box; box-sizing: border-box;
@ -66,7 +13,7 @@
} }
html { html {
color: $default-color; color: var(--default-color);
font-size: 62.5%; font-size: 62.5%;
height: 100%; height: 100%;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
@ -99,12 +46,6 @@ section {
margin: 0; margin: 0;
} }
#svelte {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.wide-container { .wide-container {
@include media-breakpoint-up(sm) { @include media-breakpoint-up(sm) {
padding: 0 $container-padding-x; padding: 0 $container-padding-x;
@ -125,16 +66,16 @@ h2 {
line-height: 1.3; line-height: 1.3;
.wrapped { .wrapped {
background: #000; background: var(--background-color-invert);
color: #fff; color: var(--default-color-invert);
margin-left: -0.15em; margin-left: -0.15em;
padding: 0 0.15em; padding: 0 0.15em;
-webkit-box-decoration-break: clone; -webkit-box-decoration-break: clone;
box-decoration-break: clone; box-decoration-break: clone;
&::selection { &::selection {
background: #fff; background: var(--selection-background);
color: #000; color: var(--selection-color);
} }
} }
} }
@ -240,27 +181,28 @@ button {
} }
.button { .button {
background: #000; color: var(--default-color-invert);
background: var(--background-color-invert);
box-sizing: border-box; box-sizing: border-box;
color: #fff;
font-size: 100%; font-size: 100%;
font-weight: 500; font-weight: 500;
padding: 0.6rem 1.2rem; padding: 0.6rem 1.2rem;
text-align: center; text-align: center;
&:hover { &:hover {
color: #ccc; color: var(--black-300);
opacity: 1; opacity: 1;
} }
&:active { &:active {
color: #9fa1a7; color: var(--black-300);
} }
} }
.button--subscribe { .button--subscribe {
background: #fff; background: var(--background-color);
border: 2px solid #f6f6f6; color: var(--default-color);
border: 2px solid var(--black-100);
font-size: 1.5rem; font-size: 1.5rem;
justify-content: center; justify-content: center;
padding: 0.6rem 1.2rem; padding: 0.6rem 1.2rem;
@ -272,7 +214,8 @@ button {
} }
&:hover { &:hover {
background: #000; background: var(--background-color-invert);
color: var(--default-color-invert);
img { img {
filter: invert(1); filter: invert(1);
@ -282,31 +225,31 @@ button {
.button--light { .button--light {
font-size:1.5rem; font-size:1.5rem;
background-color: #f6f6f6; background-color: var(--black-100);
border-radius: 0.8rem; border-radius: 0.8rem;
color: #000; color: var(--default-color);
font-weight: 500; font-weight: 500;
height: auto; height: auto;
padding: 0.6rem 1.2rem 0.6rem 1rem; padding: 0.6rem 1.2rem 0.6rem 1rem;
&:hover { &:hover {
background: #e9e9ee; background: var(--black-300);
} }
} }
.button--subscribe-topic { .button--subscribe-topic {
background: #fff; background: var(--background-color);
border: 2px solid #000; color: var(--default-color);
border: 2px solid var(--default-color);
border-radius: 0.8rem; border-radius: 0.8rem;
color: #000;
font-size: 1.4rem; font-size: 1.4rem;
line-height: 2.8rem; line-height: 2.8rem;
height: 3.2rem; height: 3.2rem;
padding: 0 1rem; padding: 0 1rem;
&:hover { &:hover {
background: #000; background: var(--background-color-invert);
color: #fff; color: var(--default-color-invert);
opacity: 1; opacity: 1;
.icon { .icon {
@ -315,8 +258,8 @@ button {
} }
&[disabled]:hover { &[disabled]:hover {
background: #fff; background: var(--background-color);
color: #000; color: var(--default-color);
} }
.icon { .icon {
@ -339,7 +282,7 @@ button {
} }
background: none; background: none;
border: 2px solid #fff; border: 2px solid var(--white-500);
height: 3.2rem; height: 3.2rem;
float: right; float: right;
padding: 0; padding: 0;
@ -390,7 +333,7 @@ button {
color: #000; color: #000;
&:hover { &:hover {
box-shadow: inset 0 0 0 2px #ccc; box-shadow: inset 0 0 0 2px var(--black-300);
} }
} }
@ -403,7 +346,7 @@ form {
input[type='date'], input[type='date'],
textarea, textarea,
select { select {
border: 2px solid #e8e8e8; border: 2px solid var(--black-100);
border-radius: 2px; border-radius: 2px;
display: block; display: block;
font-family: inherit; font-family: inherit;
@ -421,7 +364,7 @@ form {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-touch-callout: none; -webkit-touch-callout: none;
color: #a4acb1; color: var(--black-400);
position: absolute; position: absolute;
left: 1.2rem; left: 1.2rem;
display: block; display: block;
@ -498,7 +441,7 @@ form {
.pretty-form__item--error { .pretty-form__item--error {
input { input {
border-color: #d00820; border-color: var(--danger-color);
} }
} }
@ -575,13 +518,10 @@ figure {
margin: auto; margin: auto;
width: 100%; width: 100%;
} }
}
.ta-video-container,
figure {
figcaption { figcaption {
font-size:1.2rem; font-size:1.2rem;
color: rgb(0 0 0 / 60%); color: var(--black-400);
line-height: 1.5; line-height: 1.5;
} }
} }
@ -627,12 +567,6 @@ figure {
padding: 0 !important; padding: 0 !important;
} }
a {
&:hover {
color: #fff;
}
}
a, a,
.link, .link,
.linkReplacement, .linkReplacement,
@ -655,8 +589,8 @@ figure {
.link, .link,
.linkReplacement, .linkReplacement,
button { button {
border-bottom: 2px solid #000; border-bottom: 2px solid var(--default-color);
color: #000; color: var(--default-color);
cursor: default; cursor: default;
&:hover { &:hover {
@ -682,8 +616,8 @@ figure {
text-align: center; text-align: center;
.view-switcher__item--selected & { .view-switcher__item--selected & {
background: #000; background: var(--background-color-invert);
color: #fff; color: var(--default-color-invert);
} }
} }
@ -753,15 +687,15 @@ figure {
margin-bottom: 5rem; margin-bottom: 5rem;
} }
background: #000; background: var(--background-color-invert);
color: #fff; color: var(--default-color-invert);
padding: $grid-gutter-width 0; padding: $grid-gutter-width 0;
padding-bottom: $container-padding-x; padding-bottom: $container-padding-x;
padding-top: $container-padding-x; padding-top: $container-padding-x;
::selection { ::selection {
background: #fff; background: var(--background-color);
color: #000; color: var(--default-color) !important;
} }
h2 { h2 {
@ -771,53 +705,13 @@ figure {
.all-materials { .all-materials {
a { a {
color: #fff; color: var(--default-color-invert) !important;
} }
} }
a:hover { a:hover {
background: #fff; color: var(--default-color) !important;
color: #000 !important; background: var(--background-color);
}
}
.short-cards {
@include media-breakpoint-up(md) {
display: flex;
flex-wrap: wrap;
}
margin: 0 -5px;
}
.short-card {
@include media-breakpoint-up(md) {
flex: 1 0 50%;
}
@include media-breakpoint-up(lg) {
flex: 1 0 25%;
}
margin-bottom: 10px;
padding: 0 5px;
}
.row {
@include media-breakpoint-down(md) {
> * {
margin-bottom: 2.4rem;
}
}
@include media-breakpoint-down(sm) {
margin-left: divide(-$container-padding-x, 2);
margin-right: divide(-$container-padding-x, 2);
> * {
padding-left: divide($container-padding-x, 2);
padding-right: divide($container-padding-x, 2);
}
} }
} }
@ -843,15 +737,6 @@ figure {
padding-top: 0; padding-top: 0;
} }
.container {
@include media-breakpoint-down(sm) {
// padding: 0 $container-padding-x * 0.5;
}
max-width: 1500px;
width: auto;
}
.container--static-page { .container--static-page {
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
padding-top: 1.5em; padding-top: 1.5em;
@ -896,20 +781,6 @@ figure {
height: 420px; height: 420px;
} }
.mode-switcher {
@include media-breakpoint-up(md) {
text-align: right;
}
font-size:1.5rem;
}
.mode-switcher__control {
border-bottom: 1px dotted;
cursor: pointer;
font-weight: bold;
}
.content-index { .content-index {
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
position: sticky; position: sticky;
@ -937,14 +808,6 @@ figure {
} }
} }
.load-more-container {
text-align: center;
button {
padding: 0.6em 1.5em;
}
}
details { details {
@include media-breakpoint-down(md) { @include media-breakpoint-down(md) {
padding-left: 3rem; padding-left: 3rem;
@ -998,7 +861,7 @@ details {
.description { .description {
font-size:1.4rem; font-size:1.4rem;
color: rgba(0 0 0 / 40%); color: var(--black-400);
.pretty-form__item + & { .pretty-form__item + & {
margin-top: -2rem; margin-top: -2rem;
@ -1011,20 +874,23 @@ details {
/* total width */ /* total width */
&::-webkit-scrollbar { &::-webkit-scrollbar {
background-color: #fff; color: var(--default-color);
background: var(--background-color);
width: 16px; width: 16px;
} }
/* background of the scrollbar except button or resizer */ /* background of the scrollbar except button or resizer */
&::-webkit-scrollbar-track { &::-webkit-scrollbar-track {
background-color: #fff; color: var(--default-color);
background: var(--background-color);
} }
/* scrollbar itself */ /* scrollbar itself */
&::-webkit-scrollbar-thumb { &::-webkit-scrollbar-thumb {
background-color: #babac0; color: var(--default-color);
background: var(--background-color);
border-radius: 16px; border-radius: 16px;
border: 4px solid #fff; border: 4px solid var(--white-500);
} }
/* set button(top and bottom of the scrollbar) */ /* set button(top and bottom of the scrollbar) */
@ -1035,7 +901,7 @@ details {
iframe { iframe {
border: none; border: none;
color: rgb(255 255 255 / 0%); color: var(--default-color);
max-width: 100%; max-width: 100%;
} }
@ -1051,38 +917,9 @@ iframe {
.blackModeIntersection { .blackModeIntersection {
color: var(--default-color); color: var(--default-color);
background: #fef2f2; background: var(--background-color);
} }
.img-align-column { .img-align-column {
clear: both; clear: both;
} }
.cropper-modal {
background-color: #000 !important;
opacity: 0.4 !important;
}
.cropper-canvas {
filter: blur(2px);
}
.cropper-view-box,
.cropper-crop-box,
.cropper-line,
.cropper-point {
box-shadow: none !important;
outline: none !important;
border: none !important;
background-color: transparent !important;
}
.cropper-crop-box {
border: 2px solid #000 !important;
border-radius: 8px;
}
.cropper-view-box,
.cropper-face {
border-radius: 50%;
}

View File

@ -224,3 +224,25 @@
font-weight: 700; font-weight: 700;
line-height: 18px; line-height: 18px;
} }
.short-cards {
@include media-breakpoint-up(md) {
display: flex;
flex-wrap: wrap;
}
margin: 0 -5px;
}
.short-card {
@include media-breakpoint-up(md) {
flex: 1 0 50%;
}
@include media-breakpoint-up(lg) {
flex: 1 0 25%;
}
margin-bottom: 10px;
padding: 0 5px;
}

View File

@ -20,4 +20,18 @@
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
} }
.modeSwitcher {
@include media-breakpoint-up(md) {
text-align: right;
}
font-size:1.5rem;
}
.modeSwitcherControl {
border-bottom: 1px dotted;
cursor: pointer;
font-weight: bold;
}
} }