Merge remote-tracking branch 'origin/main' into neue/post-avi

* origin/main:
  [Neue] Handle emoji within custom font (#5449)
  Use pressable for video controls (#5452)
  Resolve source files for fonts, remove hack (#5454)
  changed white (gray_0) text to offwhite (gray_25) (#5453)
  Let Expo/Webpack handle CSS assets (#3942)
This commit is contained in:
Eric Bailey
2024-09-23 10:46:25 -05:00
30 changed files with 527 additions and 887 deletions
+2 -2
View File
@@ -21,7 +21,7 @@ jobs:
with:
go-version: '1.22'
- name: Dummy Static Files
run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt
run: touch bskyweb/static/js/blah.js && touch bskyweb/static/css/blah.txt && touch bskyweb/static/media/blah.txt
- name: Check
run: cd bskyweb/ && make check
- name: Build (binary)
@@ -38,6 +38,6 @@ jobs:
with:
go-version: '1.22'
- name: Dummy Static Files
run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt
run: touch bskyweb/static/js/blah.js && touch bskyweb/static/css/blah.txt && touch bskyweb/static/media/blah.txt
- name: Lint
run: cd bskyweb/ && make lint
+3 -1
View File
@@ -1,4 +1,4 @@
# Ignore everything except JS-ey code.
# Ignore everything except JS-ey or CSS code.
# Based on https://stackoverflow.com/a/70715829/458193
*
!**/*.js
@@ -7,6 +7,8 @@
!**/*.tsx
!*/
!**/*.css
# More specific ignores go below.
.expo
android
+1
View File
@@ -40,6 +40,7 @@ RUN find ./bskyweb/embedr-static && find ./bskyweb/embedr-templates && find ./bs
# hack around issue with empty directory and go:embed
RUN touch bskyweb/static/js/empty.txt
RUN touch bskyweb/static/css/empty.txt
RUN touch bskyweb/static/media/empty.txt
#
+1 -1
View File
@@ -4,4 +4,4 @@
.break-word {
word-break: break-word;
}
}
+4
View File
@@ -10,6 +10,10 @@ static/js/*.js
static/js/*.map
static/js/*.js.LICENSE.txt
static/js/empty.txt
static/css/*.css
static/css/*.map
static/css/*.css.LICENSE.txt
static/css/empty.txt
static/media/*.png
static/media/empty.txt
templates/scripts.html
View File
-381
View File
@@ -32,387 +32,6 @@
<link rel="preload" as="font" type="font/otf" href="/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf">
-->
<style>
@font-face {
font-family: "Inter-Regular";
src: local("Inter-Regular"), url(/static/media/Inter-Regular.1f5ed03b6dd9fd1f9982.otf) format("font/otf");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-Italic";
src: local("Inter-Italic"), url(/static/media/Inter-Italic.95778eb0c75dc956257e.otf) format("font/otf");
font-weight: 400;
font-style: italic;
font-display: swap;
}
/*
@font-face {
font-family: "Inter-Medium";
src: local("Inter-Medium"), url(/static/media/Inter-Medium.296aa2d65964269836b3.otf) format("font/otf");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-MediumItalic";
src: local("Inter-MediumItalic"), url(/static/media/Inter-MediumItalic.0e57e17a6311368e2114.otf) format("font/otf");
font-weight: 500;
font-style: italic;
font-display: swap;
}
*/
@font-face {
font-family: "Inter-SemiBold";
src: local("Inter-SemiBold"), url(/static/media/Inter-SemiBold.2277990330981b8409bb.otf) format("font/otf");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-SemiBoldItalic";
src: local("Inter-SemiBoldItalic"), url(/static/media/Inter-SemiBoldItalic.f62fea3df3a521d6c8a7.otf) format("font/otf");
font-weight: 600;
font-style: italic;
font-display: swap;
}
/*
@font-face {
font-family: "Inter-Bold";
src: local("Inter-Bold"), url(/static/media/Inter-Bold.8d330503e1d034ad68de.otf) format("font/otf");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-BoldItalic";
src: local("Inter-BoldItalic"), url(/static/media/Inter-BoldItalic.bb17e63f9baa0d861a20.otf) format("font/otf");
font-weight: 700;
font-style: italic;
font-display: swap;
}
*/
@font-face {
font-family: "Inter-ExtraBold";
src: local("Inter-ExtraBold"), url(/static/media/Inter-ExtraBold.ff2581a193bf6b7e0b06.otf) format("font/otf");
font-weight: 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-ExtraBoldItalic";
src: local("Inter-ExtraBoldItalic"), url(/static/media/Inter-ExtraBoldItalic.0e50b40728d24d40fdf4.otf) format("font/otf");
font-weight: 800;
font-style: italic;
font-display: swap;
}
/*
@font-face {
font-family: "Inter-Black";
src: local("Inter-Black"), url(/static/media/Inter-Black.66e9a87f1c921e844ed4.otf) format("font/otf");
font-weight: 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-BlackItalic";
src: local("Inter-BlackItalic"), url(/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf) format("font/otf");
font-weight: 900;
font-style: italic;
font-display: swap;
}
*/
/**
* Extend the react-native-web reset:
* https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js
*/
html,
body,
#root {
width: 100%;
/* To smooth any scrolling behavior */
-webkit-overflow-scrolling: touch;
margin: 0px;
padding: 0px;
/* Allows content to fill the viewport and go beyond the bottom */
min-height: 100%;
}
#root {
flex-shrink: 0;
flex-basis: auto;
flex-grow: 1;
display: flex;
flex: 1;
}
html {
/* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
-webkit-text-size-adjust: 100%;
height: calc(100% + env(safe-area-inset-top));
scrollbar-gutter: stable both-edges;
}
html, body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif;
}
#preload {
width: 100px;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
/* Buttons and inputs have a font set by UA, so we'll have to reset that */
button, input, textarea {
font: inherit;
line-height: inherit;
}
/* Color theming */
/* Default will always be white */
:root {
--text: black;
--background: white;
--backgroundLight: hsl(211, 20%, 95%);
}
/* This gives us a black background when system is dark and we have not loaded the theme/color scheme values in JS */
@media (prefers-color-scheme: dark) {
:root {
--text: white;
--background: black;
--backgroundLight: hsl(211, 20%, 20%);
color-scheme: dark;
}
}
/* Overwrite those preferences with the selected theme */
html.theme--light {
--text: black;
--background: white;
--backgroundLight: hsl(211, 20%, 95%);
}
html.theme--dark {
--text: white;
--background: black;
--backgroundLight: hsl(211, 20%, 20%);
color-scheme: dark;
}
html.theme--dim {
--text: white;
--background: hsl(211, 20%, 4%);
--backgroundLight: hsl(211, 20%, 10%);
color-scheme: dark;
}
/* Remove autofill styles on Webkit */
input:autofill,
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active{
-webkit-background-clip: text;
-webkit-text-fill-color: var(--text);
transition: background-color 5000s ease-in-out 0s;
box-shadow: inset 0 0 20px 20px var(--background);
background: var(--background);
color: var(--text);
}
/* Force left-align date/time inputs on iOS mobile */
input::-webkit-date-and-time-value {
text-align: left;
}
body {
display: flex;
/* Allows you to scroll below the viewport; default value is visible */
overflow-y: auto;
overscroll-behavior-y: none;
text-rendering: optimizeLegibility;
background-color: var(--background);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-ms-overflow-style: scrollbar;
font-synthesis-weight: none;
}
/* Remove default link styling */
a {
color: inherit;
}
a[role="link"]:hover {
text-decoration: underline;
}
a[role="link"][data-no-underline="1"]:hover {
text-decoration: none;
}
/* Styling hacks */
*[data-word-wrap] {
word-break: break-word;
}
*[data-stable-gutters] {
scrollbar-gutter: stable both-edges;
}
/* ProseMirror */
.ProseMirror {
min-height: 140px;
}
.ProseMirror-dark {
color: white;
}
.ProseMirror p {
margin: 0;
}
.ProseMirror p.is-editor-empty:first-child::before {
color: #8d8e96;
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
.ProseMirror .mention {
color: #0085ff;
}
.ProseMirror a,
.ProseMirror .autolink {
color: #0085ff;
}
/* OLLIE: TODO -- this is not accessible */
/* Remove focus state on inputs */
.ProseMirror-focused {
outline: 0;
}
textarea:focus,
input:focus {
outline: 0;
}
.tippy-content .items {
width: fit-content;
}
/* Tooltips */
[data-tooltip] {
position: relative;
z-index: 10;
}
[data-tooltip]::after {
content: attr(data-tooltip);
display: none;
position: absolute;
bottom: 0;
left: 50%;
transform: translateY(100%) translateY(8px) translateX(-50%);
padding: 4px 10px;
border-radius: 10px;
background: var(--backgroundLight);
color: var(--text);
text-align: center;
white-space: nowrap;
font-size: 12px;
z-index: 10;
}
[data-tooltip]::before {
content: '';
display: none;
position: absolute;
border-bottom: 6px solid var(--backgroundLight);
border-left: 6px solid transparent;
border-right: 6px solid transparent;
bottom: 0;
left: 50%;
transform: translateY(100%) translateY(2px) translateX(-50%);
z-index: 10;
}
[data-tooltip]:hover::after,
[data-tooltip]:hover::before {
display:block;
}
/* NativeDropdown component */
.radix-dropdown-item:focus,
.nativeDropdown-item:focus {
outline: none;
}
/* Spinner component */
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.rotate-500ms {
position: absolute;
inset:0;
animation: rotate 500ms linear infinite;
}
@keyframes avatarHoverFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes avatarHoverFadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
.force-no-clicks > *,
.force-no-clicks * {
pointer-events: none !important;
}
input[type=range][orient=vertical] {
writing-mode: vertical-lr;
direction: rtl;
appearance: slider-vertical;
width: 16px;
vertical-align: bottom;
-webkit-appearance: none;
appearance: none;
background: transparent;
cursor: pointer;
}
input[type="range"][orient=vertical]::-webkit-slider-runnable-track {
background: white;
height: 100%;
width: 4px;
border-radius: 4px;
}
input[type="range"][orient=vertical]::-moz-range-track {
background: white;
height: 100%;
width: 4px;
border-radius: 4px;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
border-radius: 50%;
background-color: white;
height: 16px;
width: 16px;
margin-left: -6px;
}
input[type="range"][orient=vertical]::-moz-range-thumb {
border: none;
border-radius: 50%;
background-color: white;
height: 16px;
width: 16px;
margin-left: -6px;
}
</style>
{% include "scripts.html" %}
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
+4 -2
View File
@@ -15,7 +15,7 @@
"web": "expo start --web",
"use-build-number": "./scripts/useBuildNumberEnv.sh",
"use-build-number-with-bump": "./scripts/useBuildNumberEnvWithBump.sh",
"build-web": "expo export:web && node ./scripts/post-web-build.js && cp -v ./web-build/static/js/*.* ./bskyweb/static/js/ && cp -v ./web-build/static/media/* ./bskyweb/static/media/",
"build-web": "expo export:web && node ./scripts/post-web-build.js",
"build-all": "yarn intl:build && yarn use-build-number-with-bump eas build --platform all",
"build-ios": "yarn use-build-number-with-bump eas build -p ios",
"build-android": "yarn use-build-number-with-bump eas build -p android",
@@ -339,7 +339,9 @@
},
"lint-staged": {
"*{.js,.jsx,.ts,.tsx}": [
"eslint --cache --fix",
"eslint --cache --fix"
],
"*{.js,.jsx,.ts,.tsx,.css}": [
"prettier --cache --write --ignore-unknown"
]
}
+24 -1
View File
@@ -20,7 +20,30 @@ console.log(`Writing ${templateFile}`)
const outputFile = entrypoints
.map(name => {
const file = path.basename(name)
return `<script defer="defer" src="/static/js/${file}"></script>`
const ext = path.extname(file)
if (ext === '.js') {
return `<script defer="defer" src="/static/js/${file}"></script>`
}
if (ext === '.css') {
return `<link rel="stylesheet" href="/static/css/${file}">`
}
return ''
})
.join('\n')
fs.writeFileSync(templateFile, outputFile)
function copyFiles(sourceDir, targetDir) {
const files = fs.readdirSync(path.join(projectRoot, sourceDir))
files.forEach(file => {
const sourcePath = path.join(projectRoot, sourceDir, file)
const targetPath = path.join(projectRoot, targetDir, file)
fs.copyFileSync(sourcePath, targetPath)
console.log(`Copied ${sourcePath} to ${targetPath}`)
})
}
copyFiles('web-build/static/js', 'bskyweb/static/js')
copyFiles('web-build/static/css', 'bskyweb/static/css')
copyFiles('web-build/static/media', 'bskyweb/static/media')
+3 -2
View File
@@ -1,5 +1,6 @@
import 'lib/sentry' // must be near top
import 'view/icons'
import '#/lib/sentry' // must be near top
import '#/view/icons'
import './style.css'
import React, {useEffect, useState} from 'react'
import {KeyboardProvider} from 'react-native-keyboard-controller'
-35
View File
@@ -1,5 +1,3 @@
import {useFonts as defaultUseFonts} from 'expo-font'
import {isWeb} from '#/platform/detection'
import {Device, device} from '#/storage'
@@ -34,39 +32,6 @@ export function setFontFamily(fontFamily: Device['fontFamily']) {
device.set(['fontFamily'], fontFamily)
}
/*
* IMPORTANT: This is unused. Expo statically extracts these fonts, but we load
* them manually so that we can parallelize the loading along with the JS
* bundle.
*
* See `#/alf/util/useFonts` for the actually used hooks.
*
* All used fonts MUST be configured here. Unused fonts are commented out, but
* the files are there if we need them.
*/
export function DO_NOT_USE() {
return defaultUseFonts({
// 'Inter-Thin': require('../../assets/fonts/inter/Inter-Thin.otf'),
// 'Inter-ThinItalic': require('../../assets/fonts/inter/Inter-ThinItalic.otf'),
// 'Inter-ExtraLight': require('../../assets/fonts/inter/Inter-ExtraLight.otf'),
// 'Inter-ExtraLightItalic': require('../../assets/fonts/inter/Inter-ExtraLightItalic.otf'),
// 'Inter-Light': require('../../assets/fonts/inter/Inter-Light.otf'),
// 'Inter-LightItalic': require('../../assets/fonts/inter/Inter-LightItalic.otf'),
'Inter-Regular': require('../../assets/fonts/inter/Inter-Regular.otf'),
'Inter-Italic': require('../../assets/fonts/inter/Inter-Italic.otf'),
// 'Inter-Medium': require('../../assets/fonts/inter/Inter-Medium.otf'),
// 'Inter-MediumItalic': require('../../assets/fonts/inter/Inter-MediumItalic.otf'),
'Inter-SemiBold': require('../../assets/fonts/inter/Inter-SemiBold.otf'),
'Inter-SemiBoldItalic': require('../../assets/fonts/inter/Inter-SemiBoldItalic.otf'),
// 'Inter-Bold': require('../../assets/fonts/inter/Inter-Bold.otf'),
// 'Inter-BoldItalic': require('../../assets/fonts/inter/Inter-BoldItalic.otf'),
'Inter-ExtraBold': require('../../assets/fonts/inter/Inter-ExtraBold.otf'),
'Inter-ExtraBoldItalic': require('../../assets/fonts/inter/Inter-ExtraBoldItalic.otf'),
// 'Inter-Black': require('../../assets/fonts/inter/Inter-Black.otf'),
// 'Inter-BlackItalic': require('../../assets/fonts/inter/Inter-BlackItalic.otf'),
})
}
/*
* Unused fonts are commented out, but the files are there if we need them.
*/
+1 -1
View File
@@ -183,7 +183,7 @@ export function createThemes({
} as const
const darkPalette: Palette = {
white: color.gray_0,
white: color.gray_25,
black: color.trueBlack,
contrast_25: color.gray_975,
+3 -2
View File
@@ -52,13 +52,14 @@ export function Title({value}: {value: string}) {
}
export function Description({value, handle}: {value?: string; handle: string}) {
const {_} = useLingui()
return value ? (
<Text numberOfLines={2}>
<RichText value={value} style={[a.leading_snug]} />
</Text>
) : (
<Text style={[a.leading_snug]}>
<Trans>By {sanitizeHandle(handle, '@')}</Trans>
<Text emoji style={[a.leading_snug]}>
{_(msg`By ${sanitizeHandle(handle, '@')}`)}
</Text>
)
}
+7 -8
View File
@@ -7,7 +7,8 @@ import {
moderateUserList,
ModerationUI,
} from '@atproto/api'
import {Trans} from '@lingui/macro'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query'
import {sanitizeHandle} from '#/lib/strings/handles'
@@ -111,6 +112,7 @@ export function TitleAndByline({
modUi?: ModerationUI
}) {
const t = useTheme()
const {_} = useLingui()
const {currentAccount} = useSession()
return (
@@ -140,15 +142,12 @@ export function TitleAndByline({
{creator && (
<Text
emoji
style={[a.leading_snug, t.atoms.text_contrast_medium]}
numberOfLines={1}>
{purpose === MODLIST ? (
<Trans>
Moderation list by {sanitizeHandle(creator.handle, '@')}
</Trans>
) : (
<Trans>List by {sanitizeHandle(creator.handle, '@')}</Trans>
)}
{purpose === MODLIST
? _(msg`Moderation list by ${sanitizeHandle(creator.handle, '@')}`)
: _(msg`List by ${sanitizeHandle(creator.handle, '@')}`)}
</Text>
)}
</View>
+1
View File
@@ -181,6 +181,7 @@ export function NameAndHandle({
{name}
</Text>
<Text
emoji
style={[a.leading_snug, t.atoms.text_contrast_medium]}
numberOfLines={1}>
{handle}
@@ -2,7 +2,7 @@ import React from 'react'
import {View} from 'react-native'
import {Image} from 'expo-image'
import {AppBskyGraphDefs, AppBskyGraphStarterpack, AtUri} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query'
@@ -69,13 +69,10 @@ export function Card({
<Text emoji style={[a.text_md, a.font_bold, a.leading_snug]}>
{record.name}
</Text>
<Text style={[a.leading_snug, t.atoms.text_contrast_medium]}>
<Trans>
Starter pack by{' '}
{creator?.did === currentAccount?.did
? _(msg`you`)
: `@${sanitizeHandle(creator.handle)}`}
</Trans>
<Text emoji style={[a.leading_snug, t.atoms.text_contrast_medium]}>
{creator?.did === currentAccount?.did
? _(msg`Starter pack by you`)
: _(msg`Starter pack by ${sanitizeHandle(creator.handle, '@')}`)}
</Text>
</View>
</View>
+1 -3
View File
@@ -159,9 +159,7 @@ export function Text({
}
if (emoji && !childIsString(children)) {
throw new Error(
'Text: when <Text emoji />, children can only be strings.',
)
logger.error('Text: when <Text emoji />, children can only be strings.')
}
}
+2 -2
View File
@@ -325,11 +325,11 @@ export const darkTheme: Theme = {
textInverted: colors.green2,
},
inverted: {
background: lightPalette.white,
background: darkPalette.white,
backgroundLight: lightPalette.contrast_50,
text: lightPalette.black,
textLight: lightPalette.contrast_700,
textInverted: lightPalette.white,
textInverted: darkPalette.white,
link: lightPalette.primary_500,
border: lightPalette.contrast_100,
borderDark: lightPalette.contrast_200,
+7 -4
View File
@@ -1,11 +1,12 @@
import React from 'react'
import {View} from 'react-native'
import {AppBskyActorDefs} from '@atproto/api'
import {Trans} from '@lingui/macro'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isInvalidHandle} from '#/lib/strings/handles'
import {isIOS} from '#/platform/detection'
import {Shadow} from '#/state/cache/types'
import {isInvalidHandle} from 'lib/strings/handles'
import {isIOS} from 'platform/detection'
import {atoms as a, useTheme, web} from '#/alf'
import {NewskieDialog} from '#/components/NewskieDialog'
import {Text} from '#/components/Typography'
@@ -18,6 +19,7 @@ export function ProfileHeaderHandle({
disableTaps?: boolean
}) {
const t = useTheme()
const {_} = useLingui()
const invalidHandle = isInvalidHandle(profile.handle)
const blockHide = profile.viewer?.blocking || profile.viewer?.blockedBy
return (
@@ -33,6 +35,7 @@ export function ProfileHeaderHandle({
</View>
) : undefined}
<Text
emoji
numberOfLines={1}
style={[
invalidHandle
@@ -47,7 +50,7 @@ export function ProfileHeaderHandle({
: [a.text_md, a.leading_tight, t.atoms.text_contrast_medium],
web({wordBreak: 'break-all'}),
]}>
{invalidHandle ? <Trans>Invalid Handle</Trans> : `@${profile.handle}`}
{invalidHandle ? _(msg`⚠Invalid Handle`) : `@${profile.handle}`}
</Text>
</View>
)
+399
View File
@@ -0,0 +1,399 @@
@font-face {
font-family: 'Inter-Regular';
src: local('Inter-Regular'),
url(/assets/fonts/inter/Inter-Regular.otf) format('font/otf');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter-Italic';
src: local('Inter-Italic'),
url(/assets/fonts/inter/Inter-Italic.otf) format('font/otf');
font-weight: 400;
font-style: italic;
font-display: swap;
}
/*
@font-face {
font-family: "Inter-Medium";
src: local("Inter-Medium"), url(/assets/fonts/inter/Inter-Medium.otf) format("font/otf");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-MediumItalic";
src: local("Inter-MediumItalic"), url(/assets/fonts/inter/Inter-MediumItalic.otf) format("font/otf");
font-weight: 500;
font-style: italic;
font-display: swap;
}
*/
@font-face {
font-family: 'Inter-SemiBold';
src: local('Inter-SemiBold'),
url(/assets/fonts/inter/Inter-SemiBold.otf) format('font/otf');
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter-SemiBoldItalic';
src: local('Inter-SemiBoldItalic'),
url(/assets/fonts/inter/Inter-SemiBoldItalic.otf) format('font/otf');
font-weight: 600;
font-style: italic;
font-display: swap;
}
/*
@font-face {
font-family: "Inter-Bold";
src: local("Inter-Bold"), url(/assets/fonts/inter/Inter-Bold.otf) format("font/otf");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-BoldItalic";
src: local("Inter-BoldItalic"), url(/assets/fonts/inter/Inter-BoldItalic.otf) format("font/otf");
font-weight: 700;
font-style: italic;
font-display: swap;
}
*/
@font-face {
font-family: 'Inter-ExtraBold';
src: local('Inter-ExtraBold'),
url(/assets/fonts/inter/Inter-ExtraBold.otf) format('font/otf');
font-weight: 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter-ExtraBoldItalic';
src: local('Inter-ExtraBoldItalic'),
url(/assets/fonts/inter/Inter-ExtraBoldItalic.otf) format('font/otf');
font-weight: 800;
font-style: italic;
font-display: swap;
}
/*
@font-face {
font-family: "Inter-Black";
src: local("Inter-Black"), url(/assets/fonts/inter/Inter-Black.otf) format("font/otf");
font-weight: 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-BlackItalic";
src: local("Inter-BlackItalic"), url(/assets/fonts/inter/Inter-BlackItalic.otf) format("font/otf");
font-weight: 900;
font-style: italic;
font-display: swap;
}
*/
/**
* Extend the react-native-web reset:
* https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js
*/
html,
body,
#root {
width: 100%;
/* To smooth any scrolling behavior */
-webkit-overflow-scrolling: touch;
margin: 0px;
padding: 0px;
/* Allows content to fill the viewport and go beyond the bottom */
min-height: 100%;
}
#root {
flex-shrink: 0;
flex-basis: auto;
flex-grow: 1;
display: flex;
flex: 1;
}
html {
/* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
-webkit-text-size-adjust: 100%;
height: calc(100% + env(safe-area-inset-top));
scrollbar-gutter: stable both-edges;
}
html,
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Liberation Sans', Helvetica, Arial, sans-serif;
}
#preload {
width: 100px;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
/* Buttons and inputs have a font set by UA, so we'll have to reset that */
button,
input,
textarea {
font: inherit;
line-height: inherit;
}
/* Color theming */
/* Default will always be white */
:root {
--text: black;
--background: white;
--backgroundLight: hsl(211, 20%, 95%);
}
/* This gives us a black background when system is dark and we have not loaded the theme/color scheme values in JS */
@media (prefers-color-scheme: dark) {
:root {
--text: white;
--background: black;
--backgroundLight: hsl(211, 20%, 20%);
color-scheme: dark;
}
}
/* Overwrite those preferences with the selected theme */
html.theme--light {
--text: black;
--background: white;
--backgroundLight: hsl(211, 20%, 95%);
}
html.theme--dark {
--text: white;
--background: black;
--backgroundLight: hsl(211, 20%, 20%);
color-scheme: dark;
}
html.theme--dim {
--text: white;
--background: hsl(211, 20%, 4%);
--backgroundLight: hsl(211, 20%, 10%);
color-scheme: dark;
}
/* Remove autofill styles on Webkit */
input:autofill,
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-background-clip: text;
-webkit-text-fill-color: var(--text);
transition: background-color 5000s ease-in-out 0s;
box-shadow: inset 0 0 20px 20px var(--background);
background: var(--background);
color: var(--text);
}
/* Force left-align date/time inputs on iOS mobile */
input::-webkit-date-and-time-value {
text-align: left;
}
body {
display: flex;
/* Allows you to scroll below the viewport; default value is visible */
overflow-y: auto;
overscroll-behavior-y: none;
text-rendering: optimizeLegibility;
background-color: var(--background);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-ms-overflow-style: scrollbar;
font-synthesis-weight: none;
}
/* Remove default link styling */
a {
color: inherit;
}
a[role='link']:hover {
text-decoration: underline;
}
a[role='link'][data-no-underline='1']:hover {
text-decoration: none;
}
/* Styling hacks */
*[data-word-wrap] {
word-break: break-word;
}
*[data-stable-gutters] {
scrollbar-gutter: stable both-edges;
}
/* ProseMirror */
.ProseMirror {
font: 18px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Liberation Sans', Helvetica, Arial, sans-serif;
min-height: 140px;
}
.ProseMirror-dark {
color: white;
}
.ProseMirror p {
margin: 0;
}
.ProseMirror p.is-editor-empty:first-child::before {
color: #8d8e96;
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
.ProseMirror .mention {
color: #0085ff;
}
.ProseMirror a,
.ProseMirror .autolink {
color: #0085ff;
}
/* OLLIE: TODO -- this is not accessible */
/* Remove focus state on inputs */
.ProseMirror-focused {
outline: 0;
}
textarea:focus,
input:focus {
outline: 0;
}
.tippy-content .items {
width: fit-content;
}
/* Tooltips */
[data-tooltip] {
position: relative;
z-index: 10;
}
[data-tooltip]::after {
content: attr(data-tooltip);
display: none;
position: absolute;
bottom: 0;
left: 50%;
transform: translateY(100%) translateY(8px) translateX(-50%);
padding: 4px 10px;
border-radius: 10px;
background: var(--backgroundLight);
color: var(--text);
text-align: center;
white-space: nowrap;
font-size: 12px;
z-index: 10;
}
[data-tooltip]::before {
content: '';
display: none;
position: absolute;
border-bottom: 6px solid var(--backgroundLight);
border-left: 6px solid transparent;
border-right: 6px solid transparent;
bottom: 0;
left: 50%;
transform: translateY(100%) translateY(2px) translateX(-50%);
z-index: 10;
}
[data-tooltip]:hover::after,
[data-tooltip]:hover::before {
display: block;
}
/* NativeDropdown component */
.radix-dropdown-item:focus,
.nativeDropdown-item:focus {
outline: none;
}
/* Spinner component */
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.rotate-500ms {
position: absolute;
inset: 0;
animation: rotate 500ms linear infinite;
}
@keyframes avatarHoverFadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes avatarHoverFadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.force-no-clicks > *,
.force-no-clicks * {
pointer-events: none !important;
}
input[type='range'][orient='vertical'] {
writing-mode: vertical-lr;
direction: rtl;
appearance: slider-vertical;
width: 16px;
vertical-align: bottom;
-webkit-appearance: none;
appearance: none;
background: transparent;
cursor: pointer;
}
input[type='range'][orient='vertical']::-webkit-slider-runnable-track {
background: white;
height: 100%;
width: 4px;
border-radius: 4px;
}
input[type='range'][orient='vertical']::-moz-range-track {
background: white;
height: 100%;
width: 4px;
border-radius: 4px;
}
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
border-radius: 50%;
background-color: white;
height: 16px;
width: 16px;
margin-left: -6px;
}
input[type='range'][orient='vertical']::-moz-range-thumb {
border: none;
border-radius: 50%;
background-color: white;
height: 16px;
width: 16px;
margin-left: -6px;
}
+4 -3
View File
@@ -1,9 +1,9 @@
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {LayoutChangeEvent, ScrollView, StyleSheet, View} from 'react-native'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {isNative} from '#/platform/detection'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {PressableWithHover} from '../util/PressableWithHover'
import {Text} from '../util/text/Text'
import {DraggableScrollView} from './DraggableScrollView'
@@ -131,13 +131,14 @@ export function TabBar({
<PressableWithHover
testID={`${testID}-selector-${i}`}
key={`${item}-${i}`}
ref={node => (itemRefs.current[i] = node)}
ref={node => (itemRefs.current[i] = node as any)}
onLayout={e => onItemLayout(e, i)}
style={styles.item}
hoverStyle={pal.viewLight}
onPress={() => onPressItem(i)}>
<View style={[styles.itemInner, selected && indicatorStyle]}>
<Text
emoji
type={isDesktop || isTablet ? 'xl-bold' : 'lg-bold'}
testID={testID ? `${testID}-${item}` : undefined}
style={[
+6 -1
View File
@@ -308,6 +308,7 @@ let PostThreadItemLoaded = ({
style={[styles.meta, styles.metaExpandedLine1, {zIndex: 1}]}>
<Link style={s.flex1} href={authorHref} title={authorTitle}>
<Text
emoji
type="xl-bold"
style={[pal.text, a.self_start]}
numberOfLines={1}
@@ -322,7 +323,11 @@ let PostThreadItemLoaded = ({
</View>
<View style={styles.meta}>
<Link style={s.flex1} href={authorHref} title={authorTitle}>
<Text type="md" style={[pal.textLight]} numberOfLines={1}>
<Text
emoji
type="md"
style={[pal.textLight]}
numberOfLines={1}>
{sanitizeHandle(post.author.handle, '@')}
</Text>
</Link>
+1 -1
View File
@@ -113,7 +113,7 @@ export function ProfileCard({
moderation.ui('displayName'),
)}
</Text>
<Text type="md" style={[pal.textLight]} numberOfLines={1}>
<Text emoji type="md" style={[pal.textLight]} numberOfLines={1}>
{sanitizeHandle(profile.handle, '@')}
</Text>
<ProfileCardPills
+21 -25
View File
@@ -1,39 +1,35 @@
import React, {
useState,
useCallback,
PropsWithChildren,
forwardRef,
Ref,
} from 'react'
import React, {forwardRef, PropsWithChildren} from 'react'
import {Pressable, PressableProps, StyleProp, ViewStyle} from 'react-native'
import {addStyle} from 'lib/styles'
import {View} from 'react-native'
import {addStyle} from '#/lib/styles'
import {useInteractionState} from '#/components/hooks/useInteractionState'
interface PressableWithHover extends PressableProps {
hoverStyle: StyleProp<ViewStyle>
}
export const PressableWithHover = forwardRef(function PressableWithHoverImpl(
{
children,
style,
hoverStyle,
...props
}: PropsWithChildren<PressableWithHover>,
ref: Ref<any>,
export const PressableWithHover = forwardRef<
View,
PropsWithChildren<PressableWithHover>
>(function PressableWithHoverImpl(
{children, style, hoverStyle, ...props},
ref,
) {
const [isHovering, setIsHovering] = useState(false)
const onHoverIn = useCallback(() => setIsHovering(true), [setIsHovering])
const onHoverOut = useCallback(() => setIsHovering(false), [setIsHovering])
style =
typeof style !== 'function' && isHovering
? addStyle(style, hoverStyle)
: style
const {
state: hovered,
onIn: onHoverIn,
onOut: onHoverOut,
} = useInteractionState()
return (
<Pressable
{...props}
style={style}
style={
typeof style !== 'function' && hovered
? addStyle(style, hoverStyle)
: style
}
onHoverIn={onHoverIn}
onHoverOut={onHoverOut}
ref={ref}>
@@ -1,8 +1,8 @@
import React from 'react'
import {SvgProps} from 'react-native-svg'
import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {atoms as a, useTheme, web} from '#/alf'
import {PressableWithHover} from '../../../PressableWithHover'
export function ControlButton({
active,
@@ -21,19 +21,21 @@ export function ControlButton({
}) {
const t = useTheme()
return (
<Button
label={active ? activeLabel : inactiveLabel}
<PressableWithHover
accessibilityRole="button"
accessibilityHint={active ? activeLabel : inactiveLabel}
onPress={onPress}
variant="ghost"
shape="round"
size="large"
style={a.p_2xs}
hoverStyle={{backgroundColor: 'rgba(255, 255, 255, 0.1)'}}>
style={[
a.p_xs,
a.rounded_full,
web({transition: 'background-color 0.1s'}),
]}
hoverStyle={{backgroundColor: 'rgba(255, 255, 255, 0.2)'}}>
{active ? (
<ActiveIcon fill={t.palette.white} width={20} />
) : (
<InactiveIcon fill={t.palette.white} width={20} />
)}
</Button>
</PressableWithHover>
)
}
@@ -358,9 +358,8 @@ export function Controls({
style={[
a.flex_1,
a.px_xs,
a.pt_2xs,
a.pb_md,
a.gap_md,
a.pb_sm,
a.gap_sm,
a.flex_row,
a.align_center,
]}>
@@ -373,7 +372,11 @@ export function Controls({
onPress={onPressPlayPause}
/>
<View style={a.flex_1} />
<Text style={{color: t.palette.white, fontVariant: ['tabular-nums']}}>
<Text
style={[
a.px_xs,
{color: t.palette.white, fontVariant: ['tabular-nums']},
]}>
{formatTime(currentTime)} / {formatTime(duration)}
</Text>
{hasSubtitleTrack && (
+1 -3
View File
@@ -56,9 +56,7 @@ export function Text({
}
if (emoji && !childIsString(children)) {
throw new Error(
'Text: when <Text emoji />, children can only be strings.',
)
logger.error('Text: when <Text emoji />, children can only be strings.')
}
}
+1
View File
@@ -959,6 +959,7 @@ function SearchHistory({
accessibilityIgnoresInvertColors
/>
<Text
emoji
style={[pal.text, styles.profileName]}
numberOfLines={1}>
{profile.displayName || profile.handle}
+6 -5
View File
@@ -16,19 +16,19 @@ import {useLingui} from '@lingui/react'
import {StackActions, useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
import {usePalette} from '#/lib/hooks/usePalette'
import {makeProfileLink} from '#/lib/routes/links'
import {NavigationProp} from '#/lib/routes/types'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {s} from '#/lib/styles'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
import {usePalette} from 'lib/hooks/usePalette'
import {NavigationProp} from 'lib/routes/types'
import {precacheProfile} from 'state/queries/profile'
import {precacheProfile} from '#/state/queries/profile'
import {SearchInput} from '#/view/com/util/forms/SearchInput'
import {Link} from '#/view/com/util/Link'
import {Text} from '#/view/com/util/text/Text'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {SearchInput} from 'view/com/util/forms/SearchInput'
import {Text} from 'view/com/util/text/Text'
import {atoms as a} from '#/alf'
let SearchLinkCard = ({
@@ -126,6 +126,7 @@ let SearchProfileCard = ({
/>
<View style={{flex: 1}}>
<Text
emoji
type="lg"
style={[s.bold, pal.text, a.self_start]}
numberOfLines={1}
-382
View File
@@ -36,388 +36,6 @@
<link rel="preload" as="font" type="font/otf" href="/static/media/Inter-Black.66e9a87f1c921e844ed4.otf">
<link rel="preload" as="font" type="font/otf" href="/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf">
-->
<style>
@font-face {
font-family: "Inter-Regular";
src: local("Inter-Regular"), url(/static/media/Inter-Regular.1f5ed03b6dd9fd1f9982.otf) format("font/otf");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-Italic";
src: local("Inter-Italic"), url(/static/media/Inter-Italic.95778eb0c75dc956257e.otf) format("font/otf");
font-weight: 400;
font-style: italic;
font-display: swap;
}
/*
@font-face {
font-family: "Inter-Medium";
src: local("Inter-Medium"), url(/static/media/Inter-Medium.296aa2d65964269836b3.otf) format("font/otf");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-MediumItalic";
src: local("Inter-MediumItalic"), url(/static/media/Inter-MediumItalic.0e57e17a6311368e2114.otf) format("font/otf");
font-weight: 500;
font-style: italic;
font-display: swap;
}
*/
@font-face {
font-family: "Inter-SemiBold";
src: local("Inter-SemiBold"), url(/static/media/Inter-SemiBold.2277990330981b8409bb.otf) format("font/otf");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-SemiBoldItalic";
src: local("Inter-SemiBoldItalic"), url(/static/media/Inter-SemiBoldItalic.f62fea3df3a521d6c8a7.otf) format("font/otf");
font-weight: 600;
font-style: italic;
font-display: swap;
}
/*
@font-face {
font-family: "Inter-Bold";
src: local("Inter-Bold"), url(/static/media/Inter-Bold.8d330503e1d034ad68de.otf) format("font/otf");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-BoldItalic";
src: local("Inter-BoldItalic"), url(/static/media/Inter-BoldItalic.bb17e63f9baa0d861a20.otf) format("font/otf");
font-weight: 700;
font-style: italic;
font-display: swap;
}
*/
@font-face {
font-family: "Inter-ExtraBold";
src: local("Inter-ExtraBold"), url(/static/media/Inter-ExtraBold.ff2581a193bf6b7e0b06.otf) format("font/otf");
font-weight: 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-ExtraBoldItalic";
src: local("Inter-ExtraBoldItalic"), url(/static/media/Inter-ExtraBoldItalic.0e50b40728d24d40fdf4.otf) format("font/otf");
font-weight: 800;
font-style: italic;
font-display: swap;
}
/*
@font-face {
font-family: "Inter-Black";
src: local("Inter-Black"), url(/static/media/Inter-Black.66e9a87f1c921e844ed4.otf) format("font/otf");
font-weight: 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter-BlackItalic";
src: local("Inter-BlackItalic"), url(/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf) format("font/otf");
font-weight: 900;
font-style: italic;
font-display: swap;
}
*/
/**
* Extend the react-native-web reset:
* https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js
*/
html,
body,
#root {
width: 100%;
/* To smooth any scrolling behavior */
-webkit-overflow-scrolling: touch;
margin: 0px;
padding: 0px;
/* Allows content to fill the viewport and go beyond the bottom */
min-height: 100%;
}
#root {
flex-shrink: 0;
flex-basis: auto;
flex-grow: 1;
display: flex;
flex: 1;
}
html {
/* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
-webkit-text-size-adjust: 100%;
height: calc(100% + env(safe-area-inset-top));
scrollbar-gutter: stable both-edges;
}
html, body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif;
}
#preload {
width: 100px;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
/* Buttons and inputs have a font set by UA, so we'll have to reset that */
button, input, textarea {
font: inherit;
line-height: inherit;
}
/* Color theming */
/* Default will always be white */
:root {
--text: black;
--background: white;
--backgroundLight: hsl(211, 20%, 95%);
}
/* This gives us a black background when system is dark and we have not loaded the theme/color scheme values in JS */
@media (prefers-color-scheme: dark) {
:root {
--text: white;
--background: black;
--backgroundLight: hsl(211, 20%, 20%);
color-scheme: dark;
}
}
/* Overwrite those preferences with the selected theme */
html.theme--light {
--text: black;
--background: white;
--backgroundLight: hsl(211, 20%, 95%);
}
html.theme--dark {
--text: white;
--background: black;
--backgroundLight: hsl(211, 20%, 20%);
color-scheme: dark;
}
html.theme--dim {
--text: white;
--background: hsl(211, 20%, 4%);
--backgroundLight: hsl(211, 20%, 10%);
color-scheme: dark;
}
/* Remove autofill styles on Webkit */
input:autofill,
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active{
-webkit-background-clip: text;
-webkit-text-fill-color: var(--text);
transition: background-color 5000s ease-in-out 0s;
box-shadow: inset 0 0 20px 20px var(--background);
background: var(--background);
color: var(--text);
}
/* Force left-align date/time inputs on iOS mobile */
input::-webkit-date-and-time-value {
text-align: left;
}
body {
display: flex;
/* Allows you to scroll below the viewport; default value is visible */
overflow-y: auto;
overscroll-behavior-y: none;
text-rendering: optimizeLegibility;
background-color: var(--background);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-ms-overflow-style: scrollbar;
font-synthesis-weight: none;
}
/* Remove default link styling */
a {
color: inherit;
}
a[role="link"]:hover {
text-decoration: underline;
}
a[role="link"][data-no-underline="1"]:hover {
text-decoration: none;
}
/* Styling hacks */
*[data-word-wrap] {
word-break: break-word;
}
*[data-stable-gutters] {
scrollbar-gutter: stable both-edges;
}
/* ProseMirror */
.ProseMirror {
min-height: 140px;
}
.ProseMirror-dark {
color: white;
}
.ProseMirror p {
margin: 0;
}
.ProseMirror p.is-editor-empty:first-child::before {
color: #8d8e96;
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
.ProseMirror .mention {
color: #0085ff;
}
.ProseMirror a,
.ProseMirror .autolink {
color: #0085ff;
}
/* OLLIE: TODO -- this is not accessible */
/* Remove focus state on inputs */
.ProseMirror-focused {
outline: 0;
}
textarea:focus,
input:focus {
outline: 0;
}
.tippy-content .items {
width: fit-content;
}
/* Tooltips */
[data-tooltip] {
position: relative;
z-index: 10;
}
[data-tooltip]::after {
content: attr(data-tooltip);
display: none;
position: absolute;
bottom: 0;
left: 50%;
transform: translateY(100%) translateY(8px) translateX(-50%);
padding: 4px 10px;
border-radius: 10px;
background: var(--backgroundLight);
color: var(--text);
text-align: center;
white-space: nowrap;
font-size: 12px;
z-index: 10;
}
[data-tooltip]::before {
content: '';
display: none;
position: absolute;
border-bottom: 6px solid var(--backgroundLight);
border-left: 6px solid transparent;
border-right: 6px solid transparent;
bottom: 0;
left: 50%;
transform: translateY(100%) translateY(2px) translateX(-50%);
z-index: 10;
}
[data-tooltip]:hover::after,
[data-tooltip]:hover::before {
display:block;
}
/* NativeDropdown component */
.radix-dropdown-item:focus,
.nativeDropdown-item:focus {
outline: none;
}
/* Spinner component */
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.rotate-500ms {
position: absolute;
inset:0;
animation: rotate 500ms linear infinite;
}
@keyframes avatarHoverFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes avatarHoverFadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
.force-no-clicks > *,
.force-no-clicks * {
pointer-events: none !important;
}
input[type=range][orient=vertical] {
writing-mode: vertical-lr;
direction: rtl;
appearance: slider-vertical;
width: 16px;
vertical-align: bottom;
-webkit-appearance: none;
appearance: none;
background: transparent;
cursor: pointer;
}
input[type="range"][orient=vertical]::-webkit-slider-runnable-track {
background: white;
height: 100%;
width: 4px;
border-radius: 4px;
}
input[type="range"][orient=vertical]::-moz-range-track {
background: white;
height: 100%;
width: 4px;
border-radius: 4px;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
border-radius: 50%;
background-color: white;
height: 16px;
width: 16px;
margin-left: -6px;
}
input[type="range"][orient=vertical]::-moz-range-thumb {
border: none;
border-radius: 50%;
background-color: white;
height: 16px;
width: 16px;
margin-left: -6px;
}
</style>
</head>
<body>