mirror of
https://codeberg.org/VnPower/PixivFE
synced 2024-12-06 19:16:23 +01:00
563 lines
12 KiB
SCSS
563 lines
12 KiB
SCSS
$font-fallback-serif: "Yu Mincho",
|
|
YuMincho,
|
|
"Hiragino Mincho ProN",
|
|
"Hiragino Mincho Pro",
|
|
"Source Han Serif",
|
|
"BIZ UDMincho Medium",
|
|
"Source Serif Pro",
|
|
"Source Serif",
|
|
"Noto Serif",
|
|
"Times New Roman",
|
|
"Georgia Pro",
|
|
Georgia,
|
|
Simsun,
|
|
PMingLiu,
|
|
AppleMyungjo,
|
|
"Source Han Serif K",
|
|
"Source Han Serif KR",
|
|
"Noto Serif KR",
|
|
Batang,
|
|
serif;
|
|
|
|
$font-fallback-sans: YuGothic,
|
|
"Hiragino Kaku Gothic Pro",
|
|
Meiryo,
|
|
"Source Han Sans",
|
|
"Source Han Sans JP",
|
|
"Noto Sans CJK JP",
|
|
"Avenir Next",
|
|
Avenir,
|
|
"Source Sans",
|
|
"Noto Sans",
|
|
Roboto,
|
|
Verdana,
|
|
"Pingfang TC",
|
|
"Pingfang HK",
|
|
"Hiragino Sans CNS",
|
|
"Lantinghei TC",
|
|
"Source Han Sans TW",
|
|
"Source Han Sans HK",
|
|
"Noto Sans CJK TC",
|
|
"Microsoft JhengHei",
|
|
"Pingfang SC",
|
|
"Hiragino Sans GB",
|
|
"Lantinghei SC",
|
|
"Source Han Sans CN",
|
|
"Noto Sans CJK SC",
|
|
"Microsoft Yahei",
|
|
DengXian,
|
|
"Apple SD Gothic Neo",
|
|
"Source Han Sans K",
|
|
"Source Han Sans KR",
|
|
"Noto Sans CJK KR",
|
|
"Malgun Gothic",
|
|
sans-serif;
|
|
|
|
$font-fallback-serif: "Yu Mincho",
|
|
YuMincho,
|
|
"Hiragino Mincho ProN",
|
|
"Hiragino Mincho Pro",
|
|
"Source Han Serif",
|
|
"BIZ UDMincho Medium",
|
|
"Source Serif Pro",
|
|
"Source Serif",
|
|
"Noto Serif",
|
|
"Times New Roman",
|
|
"Georgia Pro",
|
|
Georgia,
|
|
Simsun,
|
|
PMingLiu,
|
|
AppleMyungjo,
|
|
"Source Han Serif K",
|
|
"Source Han Serif KR",
|
|
"Noto Serif KR",
|
|
Batang,
|
|
serif;
|
|
|
|
/// Bootstrap limits grid-row-columns to 6 by default
|
|
/// src: https://github.com/twbs/bootstrap/issues/36691
|
|
$grid-row-columns: 7;
|
|
|
|
/// custom aspect ratio for novels (currently using 3x4)
|
|
/// NOTE: "1x1" needs to be specified here even though it's not custom
|
|
$aspect-ratios: (
|
|
"1x1": 100%,
|
|
"1x2": calc(2 / 1 * 100%),
|
|
"3x4": calc(4 / 3 * 100%),
|
|
"9x16": calc(16 / 9 * 100%),
|
|
);
|
|
|
|
/// Base twbs imports
|
|
@import "./bootstrap/functions";
|
|
@import "./bootstrap/variables";
|
|
@import "./bootstrap/variables-dark";
|
|
|
|
/// Define custom colors
|
|
|
|
// Add a custom color to $theme-colors
|
|
$custom-colors: (
|
|
"custom-color": #7aa2f7,
|
|
"charcoal-surface1": #262626,
|
|
"charcoal-surface2": #525252,
|
|
"beige": #f5f5dc,
|
|
"off-white": #fff8eb,
|
|
/// Tailwind colors below
|
|
"neutral-50": #fafafa,
|
|
"neutral-100": #f5f5f5,
|
|
"neutral-200": #e5e5e5,
|
|
"neutral-300": #d4d4d4,
|
|
"neutral-400": #a3a3a3,
|
|
"neutral-500": #737373,
|
|
"neutral-600": #525252,
|
|
"neutral-700": #404040,
|
|
"neutral-800": #262626,
|
|
"neutral-900": #171717,
|
|
"neutral-950": #0a0a0a,
|
|
);
|
|
$theme-colors: map-merge($theme-colors, $custom-colors);
|
|
|
|
// More z-index values
|
|
$zindex-levels: (
|
|
n1: -1,
|
|
0: 0,
|
|
1: 1,
|
|
2: 2,
|
|
3: 3,
|
|
4: 4,
|
|
5: 5,
|
|
inf: 1000,
|
|
);
|
|
|
|
@import "./bootstrap/maps";
|
|
@import "./bootstrap/mixins";
|
|
@import "./bootstrap/utilities";
|
|
|
|
/// Enable responsive border classes
|
|
/// ref: https://getbootstrap.com/docs/5.3/utilities/api/#enable-responsive
|
|
$utilities: map-merge(
|
|
$utilities, (
|
|
"rounded-start": map-merge(
|
|
map-get($utilities, "rounded-start"),
|
|
( responsive: true ),
|
|
),
|
|
"rounded-top": map-merge(
|
|
map-get($utilities, "rounded-top"),
|
|
( responsive: true ),
|
|
),
|
|
"rounded-end": map-merge(
|
|
map-get($utilities, "rounded-end"),
|
|
( responsive: true ),
|
|
),
|
|
"overflow": map-merge(
|
|
map-get($utilities, "overflow"),
|
|
( responsive: true ),
|
|
),
|
|
/// for novel images
|
|
"width": map-merge(
|
|
map-get($utilities, "width"),
|
|
( responsive: true ),
|
|
),
|
|
)
|
|
);
|
|
|
|
@import "./bootstrap/bootstrap";
|
|
|
|
/// tailwind classes
|
|
.drop-shadow-sm {
|
|
filter: drop-shadow(0 1px 1px rgb(0 0 0 / 0.05));
|
|
}
|
|
|
|
.drop-shadow {
|
|
filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));
|
|
}
|
|
|
|
.drop-shadow-md {
|
|
filter: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06));
|
|
}
|
|
|
|
.drop-shadow-lg {
|
|
filter: drop-shadow(0 10px 8px rgb(0 0 0 / 0.04)) drop-shadow(0 4px 3px rgb(0 0 0 / 0.1));
|
|
}
|
|
|
|
/// Custom classes to reduce boilerplate
|
|
.custom-btn-secondary {
|
|
@extend .btn;
|
|
@extend .fw-bold;
|
|
@extend .rounded-pill;
|
|
@extend .border-0;
|
|
@extend .py-2;
|
|
@extend .px-3;
|
|
@extend .bg-charcoal-surface2;
|
|
@extend .drop-shadow-md;
|
|
|
|
&:hover,
|
|
&:focus,
|
|
&:active {
|
|
background-color: darken(map-get($custom-colors, charcoal-surface2), 2.5%) !important;
|
|
color: white; // Ensure text remains white on hover
|
|
}
|
|
}
|
|
|
|
.custom-btn-secondary-flex {
|
|
@extend .custom-btn-secondary;
|
|
@extend .flex-fill;
|
|
}
|
|
|
|
.custom-card {
|
|
@extend .card;
|
|
@extend .border-0;
|
|
@extend .rounded-5;
|
|
@extend .drop-shadow-lg;
|
|
}
|
|
|
|
.custom-card-body {
|
|
@extend .card-body;
|
|
@extend .border-0;
|
|
@extend .rounded-5;
|
|
}
|
|
|
|
.custom-novel-card {
|
|
@extend .card;
|
|
@extend .h-100;
|
|
@extend .border-0;
|
|
@extend .rounded;
|
|
}
|
|
|
|
.custom-novel-nav {
|
|
max-height: 40vh;
|
|
}
|
|
|
|
/// Use the `:has()` pseudo-class to select anchor elements that:
|
|
/// do not contain an image; are not buttons; and are not navigation links
|
|
a:not(:has(img)):not([class*="btn"]):not(.nav-link) {
|
|
position: relative;
|
|
text-decoration: none; // remove default underline
|
|
|
|
background-image: linear-gradient(currentColor, currentColor); // solid bottom underline
|
|
background-size: 0% .1em; // Start with no visible underline (width: 0%)
|
|
background-position: 0 100%; // Position at the bottom
|
|
background-repeat: no-repeat; // Ensure no repetition of the background line
|
|
transition: background-size 0.2s ease-in-out; // Smooth transition of background width
|
|
|
|
@extend .link-offset-2; // Apply link-offset to all links globally
|
|
|
|
&:hover {
|
|
background-size: 100% .1em; // On hover, expand the underline from 0 to 100%
|
|
text-decoration: none; // Ensure that browser's native underline isn't used
|
|
}
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.stamp {
|
|
width: 96px;
|
|
height: 96px;
|
|
}
|
|
|
|
.emoji {
|
|
width: 1.5rem;
|
|
height: 1.5rem;
|
|
@extend .mx-1;
|
|
}
|
|
|
|
|
|
/// TODO: need usability tests to determine whether these variables are ok; can also cause weird moire/pixel level scaling issues with novel thumbnails (especially the default cover which has intricate details)
|
|
.thumbnail-hover-dark {
|
|
overflow: hidden;
|
|
|
|
/// Wrapper to maintain rounded corners during zoom effect
|
|
/// The wrapper hides overflow and applies rounded corners,
|
|
/// while allowing the image inside to scale without breaking the rounded shape
|
|
.thumbnail-wrapper {
|
|
overflow: hidden;
|
|
|
|
img {
|
|
transition: all 0.3s ease;
|
|
|
|
&:hover {
|
|
filter: brightness(0.85);
|
|
}
|
|
|
|
@media (prefers-reduced-motion: no-preference) {
|
|
&:hover {
|
|
transform: scale(1.005);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Custom heading sizes (e.g., h1 gets h3 size, h2 gets h4 size, etc.)
|
|
h1 {
|
|
font-size: $h3-font-size;
|
|
font-weight: $font-weight-bold;
|
|
}
|
|
|
|
h2 {
|
|
font-size: $h4-font-size;
|
|
font-weight: $font-weight-bold;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
h3 {
|
|
font-size: $h5-font-size;
|
|
font-weight: $font-weight-bold;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
h4 {
|
|
font-size: $h6-font-size;
|
|
font-weight: $font-weight-bold;
|
|
}
|
|
|
|
h5 {
|
|
font-weight: $font-weight-bold;
|
|
}
|
|
|
|
h6 {
|
|
font-weight: $font-weight-bold;
|
|
}
|
|
|
|
/// htmx
|
|
.bookmark-spinner {
|
|
display: none;
|
|
}
|
|
.htmx-request .bookmark-spinner {
|
|
display: inline;
|
|
}
|
|
.htmx-request.bookmark-spinner {
|
|
display: inline;
|
|
}
|
|
|
|
.like-spinner {
|
|
display: none;
|
|
}
|
|
.htmx-request .like-spinner {
|
|
display: inline;
|
|
}
|
|
.htmx-request.like-spinner {
|
|
display: inline;
|
|
}
|
|
|
|
.follow-spinner {
|
|
display: none;
|
|
}
|
|
.htmx-request .follow-spinner {
|
|
display: inline;
|
|
}
|
|
.htmx-request.follow-spinner {
|
|
display: inline;
|
|
}
|
|
|
|
|
|
/// Hide elements that require JS when scripting is disabled
|
|
/// NOTE: This only seems to work when JS is actually disabled browser-wide, not via an extension like uBO
|
|
/// See the callout on https://developer.mozilla.org/en-US/docs/Web/CSS/@media/scripting
|
|
/// and the CSS spec at https://drafts.csswg.org/mediaqueries-5/#scripting for more on this
|
|
///
|
|
@media (scripting: none) {
|
|
.js-required {
|
|
display: none !important;
|
|
}
|
|
}
|
|
|
|
// Noscript components
|
|
#noscript-navbar-toggler {
|
|
&:checked~#noscript-navbar-close-button:not(:checked)~#noscriptOffcanvasNavbar,
|
|
&:not(:checked)~#noscript-navbar-close-button:checked~#noscriptOffcanvasNavbar {
|
|
transform: translateX(0);
|
|
}
|
|
|
|
|
|
&:checked~#noscript-navbar-close-button:not(:checked)+label,
|
|
&:not(:checked)~#noscript-navbar-close-button:checked+label {
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
#noscript-navbar-close-button + label {
|
|
margin-left: auto;
|
|
width: 350px;
|
|
transform: translateX(-100%);
|
|
transition: transform 0.3s ease-in-out;
|
|
}
|
|
|
|
#noscriptOffcanvasNavbar {
|
|
// Navbar y-padding + y-margin
|
|
top: 3.5rem !important;
|
|
left: 0;
|
|
width: 350px;
|
|
transform: translateX(-100%);
|
|
}
|
|
|
|
.noscript-tab-pane[role="tabpanel"] {
|
|
&:not(:last-of-type),
|
|
&:target ~ &:last-of-type {
|
|
display: none;
|
|
}
|
|
|
|
&:target {
|
|
display: block;
|
|
}
|
|
}
|
|
|
|
.noscript-accordion:checked ~ .accordion-collapse {
|
|
display: block !important;
|
|
}
|
|
|
|
/// Novel preference setup
|
|
[data-font=mincho][data-font=jp] {
|
|
font-family:
|
|
"Source Han Serif JP",
|
|
"Noto Serif JP",
|
|
$font-fallback-serif;
|
|
}
|
|
|
|
[data-font=mincho][data-font=zh-cn],
|
|
[data-font=mincho][data-font=zh-hans] {
|
|
font-family:
|
|
"Songti SC",
|
|
"Source Han Serif SC",
|
|
"Noto Serif SC",
|
|
"Source Han Serif CN",
|
|
$font-fallback-serif;
|
|
}
|
|
|
|
// TODO: technically, zh-hk > zh-hant > zh
|
|
// Noto Sans have
|
|
// CJK HK
|
|
// CJK JP
|
|
// CJK KR
|
|
// CJK SC
|
|
// CJK TC
|
|
|
|
[data-font=mincho][data-font=zh-tw],
|
|
[data-font=mincho][data-font=zh-hk],
|
|
[data-font=mincho][data-font=zh-hant] {
|
|
font-family:
|
|
"Songti TC",
|
|
"Source Han Serif TC",
|
|
"Noto Serif TC",
|
|
"Source Han Serif TW",
|
|
"Source Han Serif HK",
|
|
$font-fallback-serif;
|
|
}
|
|
|
|
[data-font=mincho] {
|
|
font-family: $font-fallback-serif;
|
|
}
|
|
|
|
[data-font=gothic] {
|
|
font-family: $font-fallback-sans;
|
|
}
|
|
|
|
[data-view="2"] {
|
|
writing-mode: vertical-rl;
|
|
text-orientation: upright;
|
|
direction: rtl;
|
|
max-height: 90vh; /// limit height so that only horizontal scrolling is needed
|
|
}
|
|
|
|
/// Loading indicator for hx-requests
|
|
/// ref: https://stackoverflow.com/questions/63787241/css-how-to-create-an-infinitely-moving-repeating-linear-gradient/63787567#63787567
|
|
#loading-indicator {
|
|
height: 4px;
|
|
|
|
/// background-size: 200px auto;
|
|
animation: rolling-something 1s linear infinite;
|
|
background-image: repeating-linear-gradient(-0.25turn, #118bee 0vw, transparent 40vw);
|
|
background-size: 80vw auto;
|
|
|
|
will-change: opacity;
|
|
transition: opacity 0.3s;
|
|
opacity: 0;
|
|
|
|
&.htmx-request {
|
|
opacity: 1;
|
|
transition-duration: 1s;
|
|
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
}
|
|
|
|
@keyframes rolling-something {
|
|
0% {
|
|
background-position-x: 0vw
|
|
}
|
|
|
|
100% {
|
|
background-position-x: 40vw
|
|
}
|
|
}
|
|
|
|
/// Page transitions
|
|
/// NOTE: very much a WIP, don't take the inline comments here at face value
|
|
/* $transition-duration-out: 100ms; /// this should be equal to the swap duration set on the body element
|
|
$transition-duration-in: 200ms; /// this should be equal to the settle duration set on the body element, and is added so that the new page doesnt suddenly pop in while the images are rendering
|
|
$transition-easing-out: ease-out;
|
|
$transition-easing-in: ease-in; */
|
|
|
|
/// TODO: figure out how to apply the reduced opacity to <main> only for .htmx-settling
|
|
body {
|
|
opacity: 1;
|
|
transition: opacity 100ms ease-in 200ms; /// 200ms for page content to load; may tune in the future
|
|
|
|
&:has(.htmx-request) {
|
|
opacity: 0.25;
|
|
transition: opacity 500ms ease-out;
|
|
}
|
|
|
|
&.htmx-swapping {
|
|
opacity: 0;
|
|
transition: opacity 100ms ease-out;
|
|
}
|
|
|
|
/* &.htmx-settling {
|
|
opacity: 0.5;
|
|
} */
|
|
}
|
|
|
|
/* @mixin fade-transition($duration, $easing) {
|
|
transition: opacity $duration $easing;
|
|
will-change: opacity;
|
|
} */
|
|
|
|
/* .fade-out.htmx-swapping {
|
|
opacity: 0;
|
|
transition: opacity 200ms ease-out;
|
|
} */
|
|
|
|
|
|
|
|
/* #fade-in.htmx-swapping {
|
|
opacity: 0;
|
|
}
|
|
#fade-in {
|
|
opacity: 1;
|
|
transition: opacity 1s ease-out;
|
|
} */
|
|
|
|
|
|
|
|
/* .fade-in {
|
|
opacity: 1;
|
|
|
|
&.htmx-settling {
|
|
opacity: .5;
|
|
@include fade-transition($transition-duration-in, $transition-easing-in);
|
|
|
|
// Use a nested selector to transition to opacity 1
|
|
&.htmx-settling-active {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
} */
|
|
|
|
/* @media (prefers-reduced-motion: reduce) {
|
|
.fade-out,
|
|
.fade-in {
|
|
transition: none;
|
|
}
|
|
} */
|