diff --git a/.eslintrc.js b/.eslintrc.js
index 04914bde64..fa2c74d9e9 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -37,6 +37,7 @@ module.exports = {
'Toast.Action',
'AgeAssuranceAdmonition',
'Span',
+ 'StackedButton',
],
impliedTextProps: [],
suggestedTextWrappers: {
diff --git a/.github/workflows/build-and-push-link-aws.yaml b/.github/workflows/build-and-push-link-aws.yaml
index 1c17d6e190..7fbbc23c82 100644
--- a/.github/workflows/build-and-push-link-aws.yaml
+++ b/.github/workflows/build-and-push-link-aws.yaml
@@ -1,6 +1,11 @@
name: build-and-push-link-aws
on:
workflow_dispatch:
+ pull_request:
+ paths:
+ - 'bskylink/**'
+ - 'Dockerfile.bskylink'
+ - '.github/workflows/build-and-push-link-aws.yaml'
env:
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
@@ -45,7 +50,7 @@ jobs:
uses: docker/build-push-action@v4
with:
context: .
- push: ${{ github.event_name != 'pull_request' }}
+ push: true
file: ./Dockerfile.bskylink
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
diff --git a/README.md b/README.md
index eed6ac4fec..ae4511cc3b 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,8 @@ Bluesky is an open social network built on the AT Protocol, a flexible technolog
See [./LICENSE](./LICENSE) for the full license.
+Bluesky Social PBC has committed to a software patent non-aggression pledge. For details see [the original announcement](https://bsky.social/about/blog/10-01-2025-patent-pledge).
+
## P.S.
We ❤️ you and all of the ways you support us. Thank you for making Bluesky a great place!
diff --git a/bskylink/src/bin.ts b/bskylink/src/bin.ts
index 3e0746a989..3dea1a7527 100644
--- a/bskylink/src/bin.ts
+++ b/bskylink/src/bin.ts
@@ -1,29 +1,62 @@
import {Database, envToCfg, httpLogger, LinkService, readEnv} from './index.js'
+
async function main() {
- const env = readEnv()
- const cfg = envToCfg(env)
- if (cfg.db.migrationUrl) {
- const migrateDb = Database.postgres({
- url: cfg.db.migrationUrl,
- schema: cfg.db.schema,
+ try {
+ httpLogger.info('Starting blink service')
+
+ const env = readEnv()
+ const cfg = envToCfg(env)
+
+ httpLogger.info(
+ {
+ port: cfg.service.port,
+ safelinkEnabled: cfg.service.safelinkEnabled,
+ hasDbUrl: !!cfg.db.url,
+ hasDbMigrationUrl: !!cfg.db.migrationUrl,
+ },
+ 'Configuration loaded',
+ )
+
+ if (cfg.db.migrationUrl) {
+ httpLogger.info('Running database migrations')
+ const migrateDb = Database.postgres({
+ url: cfg.db.migrationUrl,
+ schema: cfg.db.schema,
+ })
+ await migrateDb.migrateToLatestOrThrow()
+ await migrateDb.close()
+ httpLogger.info('Database migrations completed')
+ }
+
+ httpLogger.info('Creating LinkService')
+ const link = await LinkService.create(cfg)
+
+ if (link.ctx.cfg.service.safelinkEnabled) {
+ httpLogger.info('Starting Safelink client')
+ link.ctx.safelinkClient.runFetchEvents()
+ }
+
+ await link.start()
+ httpLogger.info('Link service is running')
+
+ process.on('SIGTERM', async () => {
+ httpLogger.info('Link service is stopping')
+ await link.destroy()
+ httpLogger.info('Link service is stopped')
})
- await migrateDb.migrateToLatestOrThrow()
- await migrateDb.close()
+ } catch (error) {
+ httpLogger.error(
+ {
+ error: String(error),
+ stack: error instanceof Error ? error.stack : undefined,
+ },
+ 'Failed to start blink service',
+ )
+ process.exit(1)
}
-
- const link = await LinkService.create(cfg)
-
- if (link.ctx.cfg.service.safelinkEnabled) {
- link.ctx.safelinkClient.runFetchEvents()
- }
-
- await link.start()
- httpLogger.info('link service is running')
- process.on('SIGTERM', async () => {
- httpLogger.info('link service is stopping')
- await link.destroy()
- httpLogger.info('link service is stopped')
- })
}
-main()
+main().catch(error => {
+ console.error('Unhandled startup error:', error)
+ process.exit(1)
+})
diff --git a/bskylink/src/db/index.ts b/bskylink/src/db/index.ts
index 7fe6aa536f..d335f80146 100644
--- a/bskylink/src/db/index.ts
+++ b/bskylink/src/db/index.ts
@@ -34,6 +34,16 @@ export class Database {
static postgres(opts: PgOptions): Database {
const {schema, url, txLockNonce} = opts
+ log.info(
+ {
+ schema,
+ poolSize: opts.poolSize,
+ poolMaxUses: opts.poolMaxUses,
+ poolIdleTimeoutMs: opts.poolIdleTimeoutMs,
+ },
+ 'Creating database connection',
+ )
+
const pool =
opts.pool ??
new Pg.Pool({
diff --git a/bskyogcard/src/index.ts b/bskyogcard/src/index.ts
index 110c3f50b7..53b5dbd6e1 100644
--- a/bskyogcard/src/index.ts
+++ b/bskyogcard/src/index.ts
@@ -62,7 +62,10 @@ export class CardService {
// Start main application server
this.server = this.app.listen(this.ctx.cfg.service.port)
this.server.keepAliveTimeout = 90000
- this.terminator = createHttpTerminator({server: this.server})
+ this.terminator = createHttpTerminator({
+ server: this.server,
+ gracefulTerminationTimeout: 15000, // 15s timeout for in-flight requests
+ })
await events.once(this.server, 'listening')
// Start separate metrics server
@@ -73,13 +76,32 @@ export class CardService {
})
this.metricsServer = metricsApp.listen(this.ctx.cfg.service.metricsPort)
- this.metricsTerminator = createHttpTerminator({server: this.metricsServer})
+ this.metricsTerminator = createHttpTerminator({
+ server: this.metricsServer,
+ gracefulTerminationTimeout: 2000, // 2s timeout for metrics server
+ })
await events.once(this.metricsServer, 'listening')
}
async destroy() {
+ const startTime = Date.now()
+
this.ctx.abortController.abort()
- await this.terminator?.terminate()
- await this.metricsTerminator?.terminate()
+
+ const shutdownPromises = []
+
+ if (this.terminator) {
+ shutdownPromises.push(this.terminator.terminate())
+ }
+
+ if (this.metricsTerminator) {
+ shutdownPromises.push(this.metricsTerminator.terminate())
+ }
+
+ await Promise.all(shutdownPromises)
+
+ const elapsed = Date.now() - startTime
+ const {httpLogger} = await import('./logger.js')
+ httpLogger.info(`Graceful shutdown completed in ${elapsed}ms`)
}
}
diff --git a/bskyweb/templates/post.html b/bskyweb/templates/post.html
index 983f845355..ff21aaca14 100644
--- a/bskyweb/templates/post.html
+++ b/bskyweb/templates/post.html
@@ -56,6 +56,41 @@
+
{%- elif requiresAuth and profileHandle -%}
diff --git a/bskyweb/templates/profile.html b/bskyweb/templates/profile.html
index cc33ed5318..bb552ef47d 100644
--- a/bskyweb/templates/profile.html
+++ b/bskyweb/templates/profile.html
@@ -51,6 +51,41 @@
{% endif %}
+
+
{% endif -%}
{%- endblock %}
diff --git a/jest/test-utils.tsx b/jest/test-utils.tsx
index 0a22d792bc..264b31fae5 100644
--- a/jest/test-utils.tsx
+++ b/jest/test-utils.tsx
@@ -1,22 +1,18 @@
-import React from 'react'
-import {render} from '@testing-library/react-native'
import {GestureHandlerRootView} from 'react-native-gesture-handler'
-import {RootSiblingParent} from 'react-native-root-siblings'
import {SafeAreaProvider} from 'react-native-safe-area-context'
-import {RootStoreProvider, RootStoreModel} from '../src/state'
+import {render} from '@testing-library/react-native'
+
import {ThemeProvider} from '../src/lib/ThemeContext'
+import {type RootStoreModel, RootStoreProvider} from '../src/state'
const customRender = (ui: any, rootStore: RootStoreModel) =>
render(
- // eslint-disable-next-line react-native/no-inline-styles
-
-
-
- {ui}
-
-
-
+
+
+ {ui}
+
+
,
)
diff --git a/package.json b/package.json
index df8155d0cf..6ff8711102 100644
--- a/package.json
+++ b/package.json
@@ -74,6 +74,7 @@
"@atproto/api": "^0.16.7",
"@bitdrift/react-native": "^0.6.8",
"@braintree/sanitize-url": "^6.0.2",
+ "@bsky.app/alf": "^0.1.2",
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
"@emoji-mart/react": "^1.1.1",
"@expo/html-elements": "^0.12.5",
@@ -196,7 +197,6 @@
"react-native-progress": "bluesky-social/react-native-progress",
"react-native-qrcode-styled": "^0.3.3",
"react-native-reanimated": "^3.19.1",
- "react-native-root-siblings": "^5.0.1",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.1",
diff --git a/patches/react-native-screens+4.16.0.patch b/patches/react-native-screens+4.16.0.patch
new file mode 100644
index 0000000000..8be75c770f
--- /dev/null
+++ b/patches/react-native-screens+4.16.0.patch
@@ -0,0 +1,326 @@
+diff --git a/node_modules/react-native-screens/ios/RNSScreen.mm b/node_modules/react-native-screens/ios/RNSScreen.mm
+index b62a2e2..cb469db 100644
+--- a/node_modules/react-native-screens/ios/RNSScreen.mm
++++ b/node_modules/react-native-screens/ios/RNSScreen.mm
+@@ -729,9 +729,26 @@ - (void)notifyTransitionProgress:(double)progress closing:(BOOL)closing goingFor
+ #endif
+ }
+
+-#if !RCT_NEW_ARCH_ENABLED
++- (void)willMoveToWindow:(UIWindow *)newWindow
++{
++ if (@available(iOS 26, *)) {
++ // In iOS 26, as soon as another screen appears in transition, it is interactable
++ // To avoid glitches resulting from clicking buttons mid transition, we temporarily disable all interactions
++ // Disabling interactions for parent navigation controller won't be enough in case of nested stack
++ // Furthermore, a stack put inside a modal will exist in an entirely different hierarchy
++ // To be sure, we block interactions on the whole window.
++ // Note that newWindows is nil when moving from instead of moving to, and Obj-C handles nil correctly
++ newWindow.userInteractionEnabled = false;
++ }
++}
++
+ - (void)presentationControllerWillDismiss:(UIPresentationController *)presentationController
+ {
++ if (@available(iOS 26, *)) {
++ // Disable interactions to disallow multiple modals dismissed at once; see willMoveToWindow
++ presentationController.containerView.window.userInteractionEnabled = false;
++ }
++#if !RCT_NEW_ARCH_ENABLED
+ // On Paper, we need to call both "cancel" and "reset" here because RN's gesture
+ // recognizer does not handle the scenario when it gets cancelled by other top
+ // level gesture recognizer. In this case by the modal dismiss gesture.
+@@ -744,8 +761,8 @@ - (void)presentationControllerWillDismiss:(UIPresentationController *)presentati
+ // down.
+ [_touchHandler cancel];
+ [_touchHandler reset];
+-}
+ #endif // !RCT_NEW_ARCH_ENABLED
++}
+
+ - (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presentationController
+ {
+@@ -757,6 +774,10 @@ - (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presenta
+
+ - (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)presentationController
+ {
++ if (@available(iOS 26, *)) {
++ // Reenable interactions; see presentationControllerWillDismiss
++ presentationController.containerView.window.userInteractionEnabled = true;
++ }
+ // NOTE(kkafar): We should consider depracating the use of gesture cancel here & align
+ // with usePreventRemove API of react-navigation v7.
+ [self notifyGestureCancel];
+@@ -767,6 +788,11 @@ - (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)pr
+
+ - (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
+ {
++ if (@available(iOS 26, *)) {
++ // Reenable interactions; see presentationControllerWillDismiss
++ // Dismissed screen doesn't hold a reference to window, but presentingViewController.view does
++ presentationController.presentingViewController.view.window.userInteractionEnabled = true;
++ }
+ if ([_reactSuperview respondsToSelector:@selector(presentationControllerDidDismiss:)]) {
+ [_reactSuperview performSelector:@selector(presentationControllerDidDismiss:) withObject:presentationController];
+ }
+@@ -1518,6 +1544,10 @@ - (void)viewWillDisappear:(BOOL)animated
+
+ - (void)viewDidAppear:(BOOL)animated
+ {
++ if (@available(iOS 26, *)) {
++ // Reenable interactions, see willMoveToWindow
++ self.view.window.userInteractionEnabled = true;
++ }
+ [super viewDidAppear:animated];
+ if (!_isSwiping || _shouldNotify) {
+ // we are going forward or dismissing without swipe
+diff --git a/node_modules/react-native-screens/ios/RNSScreenStack.mm b/node_modules/react-native-screens/ios/RNSScreenStack.mm
+index 229dc58..10b365b 100644
+--- a/node_modules/react-native-screens/ios/RNSScreenStack.mm
++++ b/node_modules/react-native-screens/ios/RNSScreenStack.mm
+@@ -62,26 +62,6 @@ @interface RNSScreenStackView () <
+
+ @implementation RNSNavigationController
+
+-#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
+-- (void)viewDidLoad
+-{
+- // iOS 26 introduces new gesture recognizer which replaces our RNSPanGestureRecognizer.
+- // The problem is that we are not able to handle it here for various reasons:
+- // - the new recognizer comes with its own delegate and our current approach is to wire
+- // all recognizers to RNSScreenStackView; to be 100% sure we don't break the logic,
+- // we would have to decorate its delegate and call it after our code, which would
+- // break other recognizers that the stack view is the delegate for
+- // - when RNSScreenStackView.setupGestureHandler method is called, the recognizer hasn't been
+- // loaded yet and there is no other place to configure in a not "hacky" way
+- // - the official docs warn us to not use it for anything other than "setting up failure requirements with it"
+- // - we expose fullScreenGestureEnabled prop to enable/disable the feature,
+- // so we need control over the delegate
+- if (@available(iOS 26.0, *)) {
+- self.interactiveContentPopGestureRecognizer.enabled = NO;
+- }
+-}
+-#endif // iOS 26
+-
+ #if !TARGET_OS_TV
+ - (UIViewController *)childViewControllerForStatusBarStyle
+ {
+@@ -219,50 +199,6 @@ - (bool)onRepeatedTabSelectionOfTabScreenController:(RNSTabsScreenViewController
+ return false;
+ }
+
+-#pragma mark - UINavigationBarDelegate
+-
+-#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
+-- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
+-{
+- if (@available(iOS 26, *)) {
+- // To prevent popping multiple screens when back button is pressed repeatedly,
+- // We allow for pop operation to proceed only if no transition is in progress,
+- // which we check indirectly by checking if transitionCoordinator is set.
+- // If it's not, we are safe to proceed.
+- if (self.transitionCoordinator == nil) {
+- // We still need to disable interactions for back button so click effects are not applied,
+- // and there is unfortunately no better place for it currently
+- UIView *button = [navigationBar rnscreens_findBackButtonWrapperView];
+- if (button != nil) {
+- button.userInteractionEnabled = false;
+- }
+-
+- return true;
+- }
+-
+- return false;
+- }
+-
+- return true;
+-}
+-
+-- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item
+-{
+- if (@available(iOS 26, *)) {
+- // Reset interactions on back button -> see navigationBar:shouldPopItem
+- // IMPORTANT: This reset won't execute when preventNativeDismiss is on.
+- // However, on iOS 26, unlike in previous versions, the back button instance changes
+- // when handling preventNativeDismiss and userIteractionEnabled is reset.
+- // The instance also changes when regular screen pop happens, but in that case
+- // the value of userInteractionEnabled is carried on, and we reset it here.
+- UIView *button = [navigationBar rnscreens_findBackButtonWrapperView];
+- if (button != nil) {
+- button.userInteractionEnabled = true;
+- }
+- }
+-}
+-#endif // Check for iOS >= 26
+-
+ #pragma mark - RNSFrameCorrectionProvider
+
+ #ifdef RNS_GAMMA_ENABLED
+@@ -327,7 +263,7 @@ @implementation RNSScreenStackView {
+ UINavigationController *_controller;
+ NSMutableArray *_reactSubviews;
+ BOOL _invalidated;
+- BOOL _isFullWidthSwiping;
++ BOOL _isFullWidthSwipingWithPanGesture; // used only for content swipe with RNSPanGestureRecognizer
+ RNSPercentDrivenInteractiveTransition *_interactionController;
+ __weak RNSScreenStackManager *_manager;
+ BOOL _updateScheduled;
+@@ -522,6 +458,11 @@ - (void)reactAddControllerToClosestParent:(UIViewController *)controller
+ [self addSubview:controller.view];
+ #if !TARGET_OS_TV
+ _controller.interactivePopGestureRecognizer.delegate = self;
++ #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
++ if (@available(iOS 26, *)) {
++ _controller.interactiveContentPopGestureRecognizer.delegate = self;
++ }
++#endif // Check for iOS >= 26.0
+ #endif
+ [controller didMoveToParentViewController:parentView.reactViewController];
+ // On iOS pre 12 we observed that `willShowViewController` delegate method does not always
+@@ -943,7 +884,7 @@ - (void)dismissOnReload
+ // when preventing the native dismiss with back button, we have to return the animator.
+ // Also, we need to return the animator when full width swiping even if the animation is not custom,
+ // otherwise the screen will be just popped immediately due to no animation
+- ((operation == UINavigationControllerOperationPop && shouldCancelDismiss) || _isFullWidthSwiping ||
++ ((operation == UINavigationControllerOperationPop && shouldCancelDismiss) || _isFullWidthSwipingWithPanGesture ||
+ [RNSScreenStackAnimator isCustomAnimation:screen.stackAnimation] || _customAnimation)) {
+ return [[RNSScreenStackAnimator alloc] initWithOperation:operation];
+ }
+@@ -967,23 +908,39 @@ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
+ }
+ RNSScreenView *topScreen = _reactSubviews.lastObject;
+
++ BOOL customAnimationOnSwipePropSetAndSelectedAnimationIsCustom =
++ topScreen.customAnimationOnSwipe && [RNSScreenStackAnimator isCustomAnimation:topScreen.stackAnimation];
++
+ #if TARGET_OS_TV || TARGET_OS_VISION
+ [self cancelTouchesInParent];
+ return YES;
+ #else
+- // RNSPanGestureRecognizer will receive events iff topScreen.fullScreenSwipeEnabled == YES;
+- // Events are filtered in gestureRecognizer:shouldReceivePressOrTouchEvent: method
+ if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) {
+- if ([self isInGestureResponseDistance:gestureRecognizer topScreen:topScreen]) {
+- _isFullWidthSwiping = YES;
+- [self cancelTouchesInParent];
+- return YES;
++ // On iOS < 26, we have a custom full screen swipe recognizer that functions similarily
++ // to interactiveContentPopGestureRecognizer introduced in iOS 26.
++ // On iOS >= 26, we want to use the native one, but we are unable to handle custom animations
++ // with native interactiveContentPopGestureRecognizer, so we have to fallback to the old implementation.
++ // In this case, the old one should behave as close as the new native one, having only the difference
++ // in animation, and without any customization that is exclusive for it (e.g. gestureResponseDistance).
++ if (@available(iOS 26, *)) {
++ if (customAnimationOnSwipePropSetAndSelectedAnimationIsCustom) {
++ _isFullWidthSwipingWithPanGesture = YES;
++ [self cancelTouchesInParent];
++ return YES;
++ }
++ return NO;
++ } else {
++ if ([self isInGestureResponseDistance:gestureRecognizer topScreen:topScreen]) {
++ _isFullWidthSwipingWithPanGesture = YES;
++ [self cancelTouchesInParent];
++ return YES;
++ }
++ return NO;
+ }
+- return NO;
+ }
+
+ // Now we're dealing with RNSScreenEdgeGestureRecognizer (or _UIParallaxTransitionPanGestureRecognizer)
+- if (topScreen.customAnimationOnSwipe && [RNSScreenStackAnimator isCustomAnimation:topScreen.stackAnimation]) {
++ if (customAnimationOnSwipePropSetAndSelectedAnimationIsCustom) {
+ if ([gestureRecognizer isKindOfClass:[RNSScreenEdgeGestureRecognizer class]]) {
+ UIRectEdge edges = ((RNSScreenEdgeGestureRecognizer *)gestureRecognizer).edges;
+ BOOL isRTL = _controller.view.semanticContentAttribute == UISemanticContentAttributeForceRightToLeft;
+@@ -1028,7 +985,9 @@ - (void)setupGestureHandlers
+ rightEdgeSwipeGestureRecognizer.delegate = self;
+ [self addGestureRecognizer:rightEdgeSwipeGestureRecognizer];
+
+- // gesture recognizer for full width swipe gesture
++ // Starting from iOS 26, RNSPanGestureRecognizer has been mostly replaced by native
++ // interactiveContentPopGestureRecognizer. It still needs to handle custom dismiss animations,
++ // which we are not able to handle with the latter.
+ RNSPanGestureRecognizer *panRecognizer = [[RNSPanGestureRecognizer alloc] initWithTarget:self
+ action:@selector(handleSwipe:)];
+ panRecognizer.delegate = self;
+@@ -1091,7 +1050,7 @@ - (void)handleSwipe:(UIPanGestureRecognizer *)gestureRecognizer
+ [_interactionController cancelInteractiveTransition];
+ }
+ _interactionController = nil;
+- _isFullWidthSwiping = NO;
++ _isFullWidthSwipingWithPanGesture = NO;
+ }
+ default: {
+ break;
+@@ -1225,14 +1184,6 @@ - (BOOL)isScrollViewPanGestureRecognizer:(UIGestureRecognizer *)gestureRecognize
+ // Be careful when adding another type of gesture recognizer.
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceivePressOrTouchEvent:(NSObject *)event
+ {
+- if (@available(iOS 26, *)) {
+- // in iOS 26, you can swipe to pop screen before the previous one finished transitioning;
+- // this prevents from registering the second gesture
+- if ([self isTransitionInProgress]) {
+- return NO;
+- }
+- }
+-
+ RNSScreenView *topScreen = _reactSubviews.lastObject;
+
+ for (RNSScreenView *s in _reactSubviews.reverseObjectEnumerator) {
+@@ -1249,10 +1200,30 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceive
+ return NO;
+ }
+
++ BOOL customAnimationOnSwipePropSetAndSelectedAnimationIsCustom =
++ topScreen.customAnimationOnSwipe && [RNSScreenStackAnimator isCustomAnimation:topScreen.stackAnimation];
++#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
++ if (@available(iOS 26, *)) {
++ // On iOS 26, fullScreenSwipeEnabled takes no effect, and depending on whether custom animations are on,
++ // we select either interactiveContentPopGestureRecognizer or RNSPanGestureRecognizer
++ if (([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]] &&
++ !customAnimationOnSwipePropSetAndSelectedAnimationIsCustom) ||
++ (gestureRecognizer == _controller.interactiveContentPopGestureRecognizer &&
++ customAnimationOnSwipePropSetAndSelectedAnimationIsCustom)) {
++ return NO;
++ }
++ } else {
++ // We want to pass events to RNSPanGestureRecognizer iff full screen swipe is enabled.
++ if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) {
++ return topScreen.fullScreenSwipeEnabled;
++ }
++ }
++#else // check for iOS >= 26
+ // We want to pass events to RNSPanGestureRecognizer iff full screen swipe is enabled.
+ if ([gestureRecognizer isKindOfClass:[RNSPanGestureRecognizer class]]) {
+ return topScreen.fullScreenSwipeEnabled;
+ }
++#endif // check for iOS >= 26
+
+ // RNSScreenEdgeGestureRecognizer || _UIParallaxTransitionPanGestureRecognizer
+ return YES;
+@@ -1268,15 +1239,6 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceive
+ return [self gestureRecognizer:gestureRecognizer shouldReceivePressOrTouchEvent:touch];
+ }
+
+-- (BOOL)isTransitionInProgress
+-{
+- if (_controller.transitionCoordinator != nil) {
+- return YES;
+- }
+-
+- return NO;
+-}
+-
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
+ shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
+ {
+@@ -1289,7 +1251,6 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
+ if (gestureRecognizer.state == UIGestureRecognizerStateBegan || isBackGesture) {
+ return NO;
+ }
+-
+ return YES;
+ }
+ return NO;
diff --git a/src/App.native.tsx b/src/App.native.tsx
index 036ecff60e..30a5e81296 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -4,7 +4,6 @@ import '#/view/icons'
import React, {useEffect, useState} from 'react'
import {GestureHandlerRootView} from 'react-native-gesture-handler'
-import {RootSiblingParent} from 'react-native-root-siblings'
import {
initialWindowMetrics,
SafeAreaProvider,
@@ -38,7 +37,6 @@ import {
} from '#/state/geolocation'
import {GlobalGestureEventsProvider} from '#/state/global-gesture-events'
import {Provider as HomeBadgeProvider} from '#/state/home-badge'
-import {Provider as InvitesStateProvider} from '#/state/invites'
import {Provider as LightboxStateProvider} from '#/state/lightbox'
import {MessagesProvider} from '#/state/messages'
import {Provider as ModalStateProvider} from '#/state/modals'
@@ -85,7 +83,11 @@ if (isIOS) {
}
if (isAndroid) {
// iOS is handled by the config plugin -sfn
- ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP)
+ ScreenOrientation.lockAsync(
+ ScreenOrientation.OrientationLock.PORTRAIT_UP,
+ ).catch(error =>
+ logger.debug('Could not lock orientation', {safeMessage: error}),
+ )
}
/**
@@ -134,64 +136,62 @@ function InnerApp() {
-
-
-
-
-
-
-
-
-
- {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -225,24 +225,22 @@ function App() {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/App.web.tsx b/src/App.web.tsx
index c86960172a..b7cba6122e 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -3,7 +3,6 @@ import '#/view/icons'
import './style.css'
import React, {useEffect, useState} from 'react'
-import {RootSiblingParent} from 'react-native-root-siblings'
import {SafeAreaProvider} from 'react-native-safe-area-context'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -26,7 +25,6 @@ import {
Provider as GeolocationProvider,
} from '#/state/geolocation'
import {Provider as HomeBadgeProvider} from '#/state/home-badge'
-import {Provider as InvitesStateProvider} from '#/state/invites'
import {Provider as LightboxStateProvider} from '#/state/lightbox'
import {MessagesProvider} from '#/state/messages'
import {Provider as ModalStateProvider} from '#/state/modals'
@@ -112,62 +110,60 @@ function InnerApp() {
-
-
-
-
-
-
-
-
-
-
- {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -199,19 +195,17 @@ function App() {
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts
index dc5d9f59c3..3168b1da18 100644
--- a/src/alf/atoms.ts
+++ b/src/alf/atoms.ts
@@ -1,115 +1,15 @@
-import {
- Platform,
- type StyleProp,
- StyleSheet,
- type ViewStyle,
-} from 'react-native'
+import {type StyleProp, type ViewStyle} from 'react-native'
+import {atoms as baseAtoms} from '@bsky.app/alf'
-import * as tokens from '#/alf/tokens'
-import {ios, native, platform, web} from '#/alf/util/platform'
+import {native, platform, web} from '#/alf/util/platform'
import * as Layout from '#/components/Layout'
export const atoms = {
- debug: {
- borderColor: 'red',
- borderWidth: 1,
- },
+ ...baseAtoms,
- /*
- * Positioning
- */
- fixed: {
- position: Platform.select({web: 'fixed', native: 'absolute'}) as 'absolute',
- },
- absolute: {
- position: 'absolute',
- },
- relative: {
- position: 'relative',
- },
- static: {
- position: 'static',
- },
- sticky: web({
- position: 'sticky',
- }),
- inset_0: {
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- },
- top_0: {
- top: 0,
- },
- right_0: {
- right: 0,
- },
- bottom_0: {
- bottom: 0,
- },
- left_0: {
- left: 0,
- },
- z_10: {
- zIndex: 10,
- },
- z_20: {
- zIndex: 20,
- },
- z_30: {
- zIndex: 30,
- },
- z_40: {
- zIndex: 40,
- },
- z_50: {
- zIndex: 50,
- },
-
- overflow_visible: {
- overflow: 'visible',
- },
- overflow_x_visible: {
- overflowX: 'visible',
- },
- overflow_y_visible: {
- overflowY: 'visible',
- },
- overflow_hidden: {
- overflow: 'hidden',
- },
- overflow_x_hidden: {
- overflowX: 'hidden',
- },
- overflow_y_hidden: {
- overflowY: 'hidden',
- },
- /**
- * @platform web
- */
- overflow_auto: web({
- overflow: 'auto',
- }),
-
- /*
- * Width & Height
- */
- w_full: {
- width: '100%',
- },
- h_full: {
- height: '100%',
- },
h_full_vh: web({
height: '100vh',
}),
- max_w_full: {
- maxWidth: '100%',
- },
- max_h_full: {
- maxHeight: '100%',
- },
/**
* Used for the outermost components on screens, to ensure that they can fill
@@ -131,895 +31,6 @@ export const atoms = {
backgroundColor: 'transparent',
},
- /*
- * Border radius
- */
- rounded_0: {
- borderRadius: 0,
- },
- rounded_2xs: {
- borderRadius: tokens.borderRadius._2xs,
- },
- rounded_xs: {
- borderRadius: tokens.borderRadius.xs,
- },
- rounded_sm: {
- borderRadius: tokens.borderRadius.sm,
- },
- rounded_md: {
- borderRadius: tokens.borderRadius.md,
- },
- rounded_lg: {
- borderRadius: tokens.borderRadius.lg,
- },
- rounded_full: {
- borderRadius: tokens.borderRadius.full,
- },
-
- /*
- * Flex
- */
- gap_0: {
- gap: 0,
- },
- gap_2xs: {
- gap: tokens.space._2xs,
- },
- gap_xs: {
- gap: tokens.space.xs,
- },
- gap_sm: {
- gap: tokens.space.sm,
- },
- gap_md: {
- gap: tokens.space.md,
- },
- gap_lg: {
- gap: tokens.space.lg,
- },
- gap_xl: {
- gap: tokens.space.xl,
- },
- gap_2xl: {
- gap: tokens.space._2xl,
- },
- gap_3xl: {
- gap: tokens.space._3xl,
- },
- gap_4xl: {
- gap: tokens.space._4xl,
- },
- gap_5xl: {
- gap: tokens.space._5xl,
- },
- flex: {
- display: 'flex',
- },
- flex_col: {
- flexDirection: 'column',
- },
- flex_row: {
- flexDirection: 'row',
- },
- flex_col_reverse: {
- flexDirection: 'column-reverse',
- },
- flex_row_reverse: {
- flexDirection: 'row-reverse',
- },
- flex_wrap: {
- flexWrap: 'wrap',
- },
- flex_nowrap: {
- flexWrap: 'nowrap',
- },
- flex_0: {
- flex: web('0 0 auto') || (native(0) as number),
- },
- flex_1: {
- flex: 1,
- },
- flex_grow: {
- flexGrow: 1,
- },
- flex_grow_0: {
- flexGrow: 0,
- },
- flex_shrink: {
- flexShrink: 1,
- },
- flex_shrink_0: {
- flexShrink: 0,
- },
- justify_start: {
- justifyContent: 'flex-start',
- },
- justify_center: {
- justifyContent: 'center',
- },
- justify_between: {
- justifyContent: 'space-between',
- },
- justify_end: {
- justifyContent: 'flex-end',
- },
- align_center: {
- alignItems: 'center',
- },
- align_start: {
- alignItems: 'flex-start',
- },
- align_end: {
- alignItems: 'flex-end',
- },
- align_baseline: {
- alignItems: 'baseline',
- },
- align_stretch: {
- alignItems: 'stretch',
- },
- self_auto: {
- alignSelf: 'auto',
- },
- self_start: {
- alignSelf: 'flex-start',
- },
- self_end: {
- alignSelf: 'flex-end',
- },
- self_center: {
- alignSelf: 'center',
- },
- self_stretch: {
- alignSelf: 'stretch',
- },
- self_baseline: {
- alignSelf: 'baseline',
- },
-
- /*
- * Text
- */
- text_left: {
- textAlign: 'left',
- },
- text_center: {
- textAlign: 'center',
- },
- text_right: {
- textAlign: 'right',
- },
- text_2xs: {
- fontSize: tokens.fontSize._2xs,
- letterSpacing: tokens.TRACKING,
- },
- text_xs: {
- fontSize: tokens.fontSize.xs,
- letterSpacing: tokens.TRACKING,
- },
- text_sm: {
- fontSize: tokens.fontSize.sm,
- letterSpacing: tokens.TRACKING,
- },
- text_md: {
- fontSize: tokens.fontSize.md,
- letterSpacing: tokens.TRACKING,
- },
- text_lg: {
- fontSize: tokens.fontSize.lg,
- letterSpacing: tokens.TRACKING,
- },
- text_xl: {
- fontSize: tokens.fontSize.xl,
- letterSpacing: tokens.TRACKING,
- },
- text_2xl: {
- fontSize: tokens.fontSize._2xl,
- letterSpacing: tokens.TRACKING,
- },
- text_3xl: {
- fontSize: tokens.fontSize._3xl,
- letterSpacing: tokens.TRACKING,
- },
- text_4xl: {
- fontSize: tokens.fontSize._4xl,
- letterSpacing: tokens.TRACKING,
- },
- text_5xl: {
- fontSize: tokens.fontSize._5xl,
- letterSpacing: tokens.TRACKING,
- },
- leading_tight: {
- lineHeight: 1.15,
- },
- leading_snug: {
- lineHeight: 1.3,
- },
- leading_normal: {
- lineHeight: 1.5,
- },
- tracking_normal: {
- letterSpacing: tokens.TRACKING,
- },
- font_normal: {
- fontWeight: tokens.fontWeight.normal,
- },
- font_medium: {
- fontWeight: tokens.fontWeight.medium,
- },
- font_bold: {
- fontWeight: tokens.fontWeight.bold,
- },
- font_heavy: {
- fontWeight: tokens.fontWeight.heavy,
- },
- italic: {
- fontStyle: 'italic',
- },
-
- /*
- * Border
- */
- border_0: {
- borderWidth: 0,
- },
- border_t_0: {
- borderTopWidth: 0,
- },
- border_b_0: {
- borderBottomWidth: 0,
- },
- border_l_0: {
- borderLeftWidth: 0,
- },
- border_r_0: {
- borderRightWidth: 0,
- },
- border_x_0: {
- borderLeftWidth: 0,
- borderRightWidth: 0,
- },
- border_y_0: {
- borderTopWidth: 0,
- borderBottomWidth: 0,
- },
- border: {
- borderWidth: StyleSheet.hairlineWidth,
- },
- border_t: {
- borderTopWidth: StyleSheet.hairlineWidth,
- },
- border_b: {
- borderBottomWidth: StyleSheet.hairlineWidth,
- },
- border_l: {
- borderLeftWidth: StyleSheet.hairlineWidth,
- },
- border_r: {
- borderRightWidth: StyleSheet.hairlineWidth,
- },
- border_x: {
- borderLeftWidth: StyleSheet.hairlineWidth,
- borderRightWidth: StyleSheet.hairlineWidth,
- },
- border_y: {
- borderTopWidth: StyleSheet.hairlineWidth,
- borderBottomWidth: StyleSheet.hairlineWidth,
- },
- border_transparent: {
- borderColor: 'transparent',
- },
- curve_circular: ios({
- borderCurve: 'circular',
- }),
- curve_continuous: ios({
- borderCurve: 'continuous',
- }),
-
- /*
- * Shadow
- */
- shadow_sm: {
- shadowRadius: 8,
- shadowOpacity: 0.1,
- elevation: 8,
- },
- shadow_md: {
- shadowRadius: 16,
- shadowOpacity: 0.1,
- elevation: 16,
- },
- shadow_lg: {
- shadowRadius: 32,
- shadowOpacity: 0.1,
- elevation: 24,
- },
-
- /*
- * Padding
- */
- p_0: {
- padding: 0,
- },
- p_2xs: {
- padding: tokens.space._2xs,
- },
- p_xs: {
- padding: tokens.space.xs,
- },
- p_sm: {
- padding: tokens.space.sm,
- },
- p_md: {
- padding: tokens.space.md,
- },
- p_lg: {
- padding: tokens.space.lg,
- },
- p_xl: {
- padding: tokens.space.xl,
- },
- p_2xl: {
- padding: tokens.space._2xl,
- },
- p_3xl: {
- padding: tokens.space._3xl,
- },
- p_4xl: {
- padding: tokens.space._4xl,
- },
- p_5xl: {
- padding: tokens.space._5xl,
- },
- px_0: {
- paddingLeft: 0,
- paddingRight: 0,
- },
- px_2xs: {
- paddingLeft: tokens.space._2xs,
- paddingRight: tokens.space._2xs,
- },
- px_xs: {
- paddingLeft: tokens.space.xs,
- paddingRight: tokens.space.xs,
- },
- px_sm: {
- paddingLeft: tokens.space.sm,
- paddingRight: tokens.space.sm,
- },
- px_md: {
- paddingLeft: tokens.space.md,
- paddingRight: tokens.space.md,
- },
- px_lg: {
- paddingLeft: tokens.space.lg,
- paddingRight: tokens.space.lg,
- },
- px_xl: {
- paddingLeft: tokens.space.xl,
- paddingRight: tokens.space.xl,
- },
- px_2xl: {
- paddingLeft: tokens.space._2xl,
- paddingRight: tokens.space._2xl,
- },
- px_3xl: {
- paddingLeft: tokens.space._3xl,
- paddingRight: tokens.space._3xl,
- },
- px_4xl: {
- paddingLeft: tokens.space._4xl,
- paddingRight: tokens.space._4xl,
- },
- px_5xl: {
- paddingLeft: tokens.space._5xl,
- paddingRight: tokens.space._5xl,
- },
- py_0: {
- paddingTop: 0,
- paddingBottom: 0,
- },
- py_2xs: {
- paddingTop: tokens.space._2xs,
- paddingBottom: tokens.space._2xs,
- },
- py_xs: {
- paddingTop: tokens.space.xs,
- paddingBottom: tokens.space.xs,
- },
- py_sm: {
- paddingTop: tokens.space.sm,
- paddingBottom: tokens.space.sm,
- },
- py_md: {
- paddingTop: tokens.space.md,
- paddingBottom: tokens.space.md,
- },
- py_lg: {
- paddingTop: tokens.space.lg,
- paddingBottom: tokens.space.lg,
- },
- py_xl: {
- paddingTop: tokens.space.xl,
- paddingBottom: tokens.space.xl,
- },
- py_2xl: {
- paddingTop: tokens.space._2xl,
- paddingBottom: tokens.space._2xl,
- },
- py_3xl: {
- paddingTop: tokens.space._3xl,
- paddingBottom: tokens.space._3xl,
- },
- py_4xl: {
- paddingTop: tokens.space._4xl,
- paddingBottom: tokens.space._4xl,
- },
- py_5xl: {
- paddingTop: tokens.space._5xl,
- paddingBottom: tokens.space._5xl,
- },
- pt_0: {
- paddingTop: 0,
- },
- pt_2xs: {
- paddingTop: tokens.space._2xs,
- },
- pt_xs: {
- paddingTop: tokens.space.xs,
- },
- pt_sm: {
- paddingTop: tokens.space.sm,
- },
- pt_md: {
- paddingTop: tokens.space.md,
- },
- pt_lg: {
- paddingTop: tokens.space.lg,
- },
- pt_xl: {
- paddingTop: tokens.space.xl,
- },
- pt_2xl: {
- paddingTop: tokens.space._2xl,
- },
- pt_3xl: {
- paddingTop: tokens.space._3xl,
- },
- pt_4xl: {
- paddingTop: tokens.space._4xl,
- },
- pt_5xl: {
- paddingTop: tokens.space._5xl,
- },
- pb_0: {
- paddingBottom: 0,
- },
- pb_2xs: {
- paddingBottom: tokens.space._2xs,
- },
- pb_xs: {
- paddingBottom: tokens.space.xs,
- },
- pb_sm: {
- paddingBottom: tokens.space.sm,
- },
- pb_md: {
- paddingBottom: tokens.space.md,
- },
- pb_lg: {
- paddingBottom: tokens.space.lg,
- },
- pb_xl: {
- paddingBottom: tokens.space.xl,
- },
- pb_2xl: {
- paddingBottom: tokens.space._2xl,
- },
- pb_3xl: {
- paddingBottom: tokens.space._3xl,
- },
- pb_4xl: {
- paddingBottom: tokens.space._4xl,
- },
- pb_5xl: {
- paddingBottom: tokens.space._5xl,
- },
- pl_0: {
- paddingLeft: 0,
- },
- pl_2xs: {
- paddingLeft: tokens.space._2xs,
- },
- pl_xs: {
- paddingLeft: tokens.space.xs,
- },
- pl_sm: {
- paddingLeft: tokens.space.sm,
- },
- pl_md: {
- paddingLeft: tokens.space.md,
- },
- pl_lg: {
- paddingLeft: tokens.space.lg,
- },
- pl_xl: {
- paddingLeft: tokens.space.xl,
- },
- pl_2xl: {
- paddingLeft: tokens.space._2xl,
- },
- pl_3xl: {
- paddingLeft: tokens.space._3xl,
- },
- pl_4xl: {
- paddingLeft: tokens.space._4xl,
- },
- pl_5xl: {
- paddingLeft: tokens.space._5xl,
- },
- pr_0: {
- paddingRight: 0,
- },
- pr_2xs: {
- paddingRight: tokens.space._2xs,
- },
- pr_xs: {
- paddingRight: tokens.space.xs,
- },
- pr_sm: {
- paddingRight: tokens.space.sm,
- },
- pr_md: {
- paddingRight: tokens.space.md,
- },
- pr_lg: {
- paddingRight: tokens.space.lg,
- },
- pr_xl: {
- paddingRight: tokens.space.xl,
- },
- pr_2xl: {
- paddingRight: tokens.space._2xl,
- },
- pr_3xl: {
- paddingRight: tokens.space._3xl,
- },
- pr_4xl: {
- paddingRight: tokens.space._4xl,
- },
- pr_5xl: {
- paddingRight: tokens.space._5xl,
- },
-
- /*
- * Margin
- */
- m_0: {
- margin: 0,
- },
- m_2xs: {
- margin: tokens.space._2xs,
- },
- m_xs: {
- margin: tokens.space.xs,
- },
- m_sm: {
- margin: tokens.space.sm,
- },
- m_md: {
- margin: tokens.space.md,
- },
- m_lg: {
- margin: tokens.space.lg,
- },
- m_xl: {
- margin: tokens.space.xl,
- },
- m_2xl: {
- margin: tokens.space._2xl,
- },
- m_3xl: {
- margin: tokens.space._3xl,
- },
- m_4xl: {
- margin: tokens.space._4xl,
- },
- m_5xl: {
- margin: tokens.space._5xl,
- },
- m_auto: {
- margin: 'auto',
- },
- mx_0: {
- marginLeft: 0,
- marginRight: 0,
- },
- mx_2xs: {
- marginLeft: tokens.space._2xs,
- marginRight: tokens.space._2xs,
- },
- mx_xs: {
- marginLeft: tokens.space.xs,
- marginRight: tokens.space.xs,
- },
- mx_sm: {
- marginLeft: tokens.space.sm,
- marginRight: tokens.space.sm,
- },
- mx_md: {
- marginLeft: tokens.space.md,
- marginRight: tokens.space.md,
- },
- mx_lg: {
- marginLeft: tokens.space.lg,
- marginRight: tokens.space.lg,
- },
- mx_xl: {
- marginLeft: tokens.space.xl,
- marginRight: tokens.space.xl,
- },
- mx_2xl: {
- marginLeft: tokens.space._2xl,
- marginRight: tokens.space._2xl,
- },
- mx_3xl: {
- marginLeft: tokens.space._3xl,
- marginRight: tokens.space._3xl,
- },
- mx_4xl: {
- marginLeft: tokens.space._4xl,
- marginRight: tokens.space._4xl,
- },
- mx_5xl: {
- marginLeft: tokens.space._5xl,
- marginRight: tokens.space._5xl,
- },
- mx_auto: {
- marginLeft: 'auto',
- marginRight: 'auto',
- },
- my_0: {
- marginTop: 0,
- marginBottom: 0,
- },
- my_2xs: {
- marginTop: tokens.space._2xs,
- marginBottom: tokens.space._2xs,
- },
- my_xs: {
- marginTop: tokens.space.xs,
- marginBottom: tokens.space.xs,
- },
- my_sm: {
- marginTop: tokens.space.sm,
- marginBottom: tokens.space.sm,
- },
- my_md: {
- marginTop: tokens.space.md,
- marginBottom: tokens.space.md,
- },
- my_lg: {
- marginTop: tokens.space.lg,
- marginBottom: tokens.space.lg,
- },
- my_xl: {
- marginTop: tokens.space.xl,
- marginBottom: tokens.space.xl,
- },
- my_2xl: {
- marginTop: tokens.space._2xl,
- marginBottom: tokens.space._2xl,
- },
- my_3xl: {
- marginTop: tokens.space._3xl,
- marginBottom: tokens.space._3xl,
- },
- my_4xl: {
- marginTop: tokens.space._4xl,
- marginBottom: tokens.space._4xl,
- },
- my_5xl: {
- marginTop: tokens.space._5xl,
- marginBottom: tokens.space._5xl,
- },
- my_auto: {
- marginTop: 'auto',
- marginBottom: 'auto',
- },
- mt_0: {
- marginTop: 0,
- },
- mt_2xs: {
- marginTop: tokens.space._2xs,
- },
- mt_xs: {
- marginTop: tokens.space.xs,
- },
- mt_sm: {
- marginTop: tokens.space.sm,
- },
- mt_md: {
- marginTop: tokens.space.md,
- },
- mt_lg: {
- marginTop: tokens.space.lg,
- },
- mt_xl: {
- marginTop: tokens.space.xl,
- },
- mt_2xl: {
- marginTop: tokens.space._2xl,
- },
- mt_3xl: {
- marginTop: tokens.space._3xl,
- },
- mt_4xl: {
- marginTop: tokens.space._4xl,
- },
- mt_5xl: {
- marginTop: tokens.space._5xl,
- },
- mt_auto: {
- marginTop: 'auto',
- },
- mb_0: {
- marginBottom: 0,
- },
- mb_2xs: {
- marginBottom: tokens.space._2xs,
- },
- mb_xs: {
- marginBottom: tokens.space.xs,
- },
- mb_sm: {
- marginBottom: tokens.space.sm,
- },
- mb_md: {
- marginBottom: tokens.space.md,
- },
- mb_lg: {
- marginBottom: tokens.space.lg,
- },
- mb_xl: {
- marginBottom: tokens.space.xl,
- },
- mb_2xl: {
- marginBottom: tokens.space._2xl,
- },
- mb_3xl: {
- marginBottom: tokens.space._3xl,
- },
- mb_4xl: {
- marginBottom: tokens.space._4xl,
- },
- mb_5xl: {
- marginBottom: tokens.space._5xl,
- },
- mb_auto: {
- marginBottom: 'auto',
- },
- ml_0: {
- marginLeft: 0,
- },
- ml_2xs: {
- marginLeft: tokens.space._2xs,
- },
- ml_xs: {
- marginLeft: tokens.space.xs,
- },
- ml_sm: {
- marginLeft: tokens.space.sm,
- },
- ml_md: {
- marginLeft: tokens.space.md,
- },
- ml_lg: {
- marginLeft: tokens.space.lg,
- },
- ml_xl: {
- marginLeft: tokens.space.xl,
- },
- ml_2xl: {
- marginLeft: tokens.space._2xl,
- },
- ml_3xl: {
- marginLeft: tokens.space._3xl,
- },
- ml_4xl: {
- marginLeft: tokens.space._4xl,
- },
- ml_5xl: {
- marginLeft: tokens.space._5xl,
- },
- ml_auto: {
- marginLeft: 'auto',
- },
- mr_0: {
- marginRight: 0,
- },
- mr_2xs: {
- marginRight: tokens.space._2xs,
- },
- mr_xs: {
- marginRight: tokens.space.xs,
- },
- mr_sm: {
- marginRight: tokens.space.sm,
- },
- mr_md: {
- marginRight: tokens.space.md,
- },
- mr_lg: {
- marginRight: tokens.space.lg,
- },
- mr_xl: {
- marginRight: tokens.space.xl,
- },
- mr_2xl: {
- marginRight: tokens.space._2xl,
- },
- mr_3xl: {
- marginRight: tokens.space._3xl,
- },
- mr_4xl: {
- marginRight: tokens.space._4xl,
- },
- mr_5xl: {
- marginRight: tokens.space._5xl,
- },
- mr_auto: {
- marginRight: 'auto',
- },
-
- /*
- * Pointer events & user select
- */
- pointer_events_none: {
- pointerEvents: 'none',
- },
- pointer_events_auto: {
- pointerEvents: 'auto',
- },
- user_select_none: {
- userSelect: 'none',
- },
- user_select_text: {
- userSelect: 'text',
- },
- user_select_all: {
- userSelect: 'all',
- },
- outline_inset_1: {
- outlineOffset: -1,
- },
-
- /*
- * Text decoration
- */
- underline: {
- textDecorationLine: 'underline',
- },
- strike_through: {
- textDecorationLine: 'line-through',
- },
-
- /*
- * Display
- */
- hidden: {
- display: 'none',
- },
- inline: web({
- display: 'inline',
- }),
- block: web({
- display: 'block',
- }),
- contents: web({
- display: 'contents',
- }),
-
/*
* Transition
*/
@@ -1099,8 +110,4 @@ export const atoms = {
transform: [],
},
}) as {transform: Exclude},
-
- pointer: web({
- cursor: 'pointer',
- }),
} as const
diff --git a/src/alf/fonts.ts b/src/alf/fonts.ts
index 7366edef2a..ddf4c0b186 100644
--- a/src/alf/fonts.ts
+++ b/src/alf/fonts.ts
@@ -7,11 +7,11 @@ const WEB_FONT_FAMILIES = `system-ui, -apple-system, BlinkMacSystemFont, "Segoe
const factor = 0.0625 // 1 - (15/16)
const fontScaleMultipliers: Record = {
- '-2': 1 - factor * 3,
- '-1': 1 - factor * 2,
- '0': 1 - factor * 1, // default
- '1': 1,
- '2': 1 + factor * 1,
+ '-2': 1 - factor * 1, // unused
+ '-1': 1 - factor * 1,
+ '0': 1, // default
+ '1': 1 + factor * 1,
+ '2': 1 + factor * 1, // unused
}
export function computeFontScaleMultiplier(scale: Device['fontScale']) {
diff --git a/src/alf/index.tsx b/src/alf/index.tsx
index bee8ed78cf..eed3fcbeb2 100644
--- a/src/alf/index.tsx
+++ b/src/alf/index.tsx
@@ -1,4 +1,5 @@
import React from 'react'
+import {type Theme, type ThemeName} from '@bsky.app/alf'
import {
computeFontScaleMultiplier,
@@ -7,16 +8,14 @@ import {
setFontFamily as persistFontFamily,
setFontScale as persistFontScale,
} from '#/alf/fonts'
-import {createThemes, defaultTheme} from '#/alf/themes'
-import {type Theme, type ThemeName} from '#/alf/types'
-import {BLUE_HUE, GREEN_HUE, RED_HUE} from '#/alf/util/colorGeneration'
+import {themes} from '#/alf/themes'
import {type Device} from '#/storage'
+export {type TextStyleProp, type Theme, type ViewStyleProp} from '@bsky.app/alf'
export {atoms} from '#/alf/atoms'
export * from '#/alf/breakpoints'
export * from '#/alf/fonts'
export * as tokens from '#/alf/tokens'
-export * from '#/alf/types'
export * from '#/alf/util/flatten'
export * from '#/alf/util/platform'
export * from '#/alf/util/themeSelector'
@@ -25,7 +24,7 @@ export * from '#/alf/util/useGutters'
export type Alf = {
themeName: ThemeName
theme: Theme
- themes: ReturnType
+ themes: typeof themes
fonts: {
scale: Exclude
scaleMultiplier: number
@@ -44,14 +43,8 @@ export type Alf = {
*/
export const Context = React.createContext({
themeName: 'light',
- theme: defaultTheme,
- themes: createThemes({
- hues: {
- primary: BLUE_HUE,
- negative: RED_HUE,
- positive: GREEN_HUE,
- },
- }),
+ theme: themes.light,
+ themes,
fonts: {
scale: getFontScale(),
scaleMultiplier: computeFontScaleMultiplier(getFontScale()),
@@ -76,10 +69,10 @@ export function ThemeProvider({
const setFontScaleAndPersist = React.useCallback<
Alf['fonts']['setFontScale']
>(
- fontScale => {
- setFontScale(fontScale)
- persistFontScale(fontScale)
- setFontScaleMultiplier(computeFontScaleMultiplier(fontScale))
+ fs => {
+ setFontScale(fs)
+ persistFontScale(fs)
+ setFontScaleMultiplier(computeFontScaleMultiplier(fs))
},
[setFontScale],
)
@@ -89,21 +82,12 @@ export function ThemeProvider({
const setFontFamilyAndPersist = React.useCallback<
Alf['fonts']['setFontFamily']
>(
- fontFamily => {
- setFontFamily(fontFamily)
- persistFontFamily(fontFamily)
+ ff => {
+ setFontFamily(ff)
+ persistFontFamily(ff)
},
[setFontFamily],
)
- const themes = React.useMemo(() => {
- return createThemes({
- hues: {
- primary: BLUE_HUE,
- negative: RED_HUE,
- positive: GREEN_HUE,
- },
- })
- }, [])
const value = React.useMemo(
() => ({
@@ -121,7 +105,6 @@ export function ThemeProvider({
}),
[
themeName,
- themes,
fontScale,
setFontScaleAndPersist,
fontFamily,
diff --git a/src/alf/themes.ts b/src/alf/themes.ts
index bb3c62c490..708bc99705 100644
--- a/src/alf/themes.ts
+++ b/src/alf/themes.ts
@@ -1,585 +1,44 @@
-import {atoms} from '#/alf/atoms'
-import {type Palette, type Theme} from '#/alf/types'
import {
- BLUE_HUE,
- defaultScale,
- dimScale,
- GREEN_HUE,
- RED_HUE,
-} from '#/alf/util/colorGeneration'
+ createThemes,
+ DEFAULT_PALETTE,
+ DEFAULT_SUBDUED_PALETTE,
+} from '@bsky.app/alf'
-const themes = createThemes({
- hues: {
- primary: BLUE_HUE,
- negative: RED_HUE,
- positive: GREEN_HUE,
- },
+const DEFAULT_THEMES = createThemes({
+ defaultPalette: DEFAULT_PALETTE,
+ subduedPalette: DEFAULT_SUBDUED_PALETTE,
})
-/**
- * @deprecated use ALF and access palette from `useTheme()`
- */
-export const lightPalette = themes.lightPalette
-/**
- * @deprecated use ALF and access palette from `useTheme()`
- */
-export const darkPalette = themes.darkPalette
-/**
- * @deprecated use ALF and access palette from `useTheme()`
- */
-export const dimPalette = themes.dimPalette
-/**
- * @deprecated use ALF and access theme from `useTheme()`
- */
-export const light = themes.light
-/**
- * @deprecated use ALF and access theme from `useTheme()`
- */
-export const dark = themes.dark
-/**
- * @deprecated use ALF and access theme from `useTheme()`
- */
-export const dim = themes.dim
-
-export const defaultTheme = themes.light
-
-export function createThemes({
- hues,
-}: {
- hues: {
- primary: number
- negative: number
- positive: number
- }
-}): {
- lightPalette: Palette
- darkPalette: Palette
- dimPalette: Palette
- light: Theme
- dark: Theme
- dim: Theme
-} {
- const color = {
- like: '#ec4899',
- trueBlack: '#000000',
-
- gray_0: `hsl(${hues.primary}, 20%, ${defaultScale[14]}%)`,
- gray_25: `hsl(${hues.primary}, 20%, ${defaultScale[13]}%)`,
- gray_50: `hsl(${hues.primary}, 20%, ${defaultScale[12]}%)`,
- gray_100: `hsl(${hues.primary}, 20%, ${defaultScale[11]}%)`,
- gray_200: `hsl(${hues.primary}, 20%, ${defaultScale[10]}%)`,
- gray_300: `hsl(${hues.primary}, 20%, ${defaultScale[9]}%)`,
- gray_400: `hsl(${hues.primary}, 20%, ${defaultScale[8]}%)`,
- gray_500: `hsl(${hues.primary}, 20%, ${defaultScale[7]}%)`,
- gray_600: `hsl(${hues.primary}, 24%, ${defaultScale[6]}%)`,
- gray_700: `hsl(${hues.primary}, 24%, ${defaultScale[5]}%)`,
- gray_800: `hsl(${hues.primary}, 28%, ${defaultScale[4]}%)`,
- gray_900: `hsl(${hues.primary}, 28%, ${defaultScale[3]}%)`,
- gray_950: `hsl(${hues.primary}, 28%, ${defaultScale[2]}%)`,
- gray_975: `hsl(${hues.primary}, 28%, ${defaultScale[1]}%)`,
- gray_1000: `hsl(${hues.primary}, 28%, ${defaultScale[0]}%)`,
-
- primary_25: `hsl(${hues.primary}, 99%, 97%)`,
- primary_50: `hsl(${hues.primary}, 99%, 95%)`,
- primary_100: `hsl(${hues.primary}, 99%, 90%)`,
- primary_200: `hsl(${hues.primary}, 99%, 80%)`,
- primary_300: `hsl(${hues.primary}, 99%, 70%)`,
- primary_400: `hsl(${hues.primary}, 99%, 60%)`,
- primary_500: `hsl(${hues.primary}, 99%, 53%)`,
- primary_600: `hsl(${hues.primary}, 99%, 42%)`,
- primary_700: `hsl(${hues.primary}, 99%, 34%)`,
- primary_800: `hsl(${hues.primary}, 99%, 26%)`,
- primary_900: `hsl(${hues.primary}, 99%, 18%)`,
- primary_950: `hsl(${hues.primary}, 99%, 10%)`,
- primary_975: `hsl(${hues.primary}, 99%, 7%)`,
-
- green_25: `hsl(${hues.positive}, 82%, 97%)`,
- green_50: `hsl(${hues.positive}, 82%, 95%)`,
- green_100: `hsl(${hues.positive}, 82%, 90%)`,
- green_200: `hsl(${hues.positive}, 82%, 80%)`,
- green_300: `hsl(${hues.positive}, 82%, 70%)`,
- green_400: `hsl(${hues.positive}, 82%, 60%)`,
- green_500: `hsl(${hues.positive}, 82%, 50%)`,
- green_600: `hsl(${hues.positive}, 82%, 42%)`,
- green_700: `hsl(${hues.positive}, 82%, 34%)`,
- green_800: `hsl(${hues.positive}, 82%, 26%)`,
- green_900: `hsl(${hues.positive}, 82%, 18%)`,
- green_950: `hsl(${hues.positive}, 82%, 10%)`,
- green_975: `hsl(${hues.positive}, 82%, 7%)`,
-
- red_25: `hsl(${hues.negative}, 91%, 97%)`,
- red_50: `hsl(${hues.negative}, 91%, 95%)`,
- red_100: `hsl(${hues.negative}, 91%, 90%)`,
- red_200: `hsl(${hues.negative}, 91%, 80%)`,
- red_300: `hsl(${hues.negative}, 91%, 70%)`,
- red_400: `hsl(${hues.negative}, 91%, 60%)`,
- red_500: `hsl(${hues.negative}, 91%, 50%)`,
- red_600: `hsl(${hues.negative}, 91%, 42%)`,
- red_700: `hsl(${hues.negative}, 91%, 34%)`,
- red_800: `hsl(${hues.negative}, 91%, 26%)`,
- red_900: `hsl(${hues.negative}, 91%, 18%)`,
- red_950: `hsl(${hues.negative}, 91%, 10%)`,
- red_975: `hsl(${hues.negative}, 91%, 7%)`,
- } as const
-
- const lightPalette = {
- white: color.gray_0,
- black: color.gray_1000,
- like: color.like,
-
- contrast_25: color.gray_25,
- contrast_50: color.gray_50,
- contrast_100: color.gray_100,
- contrast_200: color.gray_200,
- contrast_300: color.gray_300,
- contrast_400: color.gray_400,
- contrast_500: color.gray_500,
- contrast_600: color.gray_600,
- contrast_700: color.gray_700,
- contrast_800: color.gray_800,
- contrast_900: color.gray_900,
- contrast_950: color.gray_950,
- contrast_975: color.gray_975,
-
- primary_25: color.primary_25,
- primary_50: color.primary_50,
- primary_100: color.primary_100,
- primary_200: color.primary_200,
- primary_300: color.primary_300,
- primary_400: color.primary_400,
- primary_500: color.primary_500,
- primary_600: color.primary_600,
- primary_700: color.primary_700,
- primary_800: color.primary_800,
- primary_900: color.primary_900,
- primary_950: color.primary_950,
- primary_975: color.primary_975,
-
- positive_25: color.green_25,
- positive_50: color.green_50,
- positive_100: color.green_100,
- positive_200: color.green_200,
- positive_300: color.green_300,
- positive_400: color.green_400,
- positive_500: color.green_500,
- positive_600: color.green_600,
- positive_700: color.green_700,
- positive_800: color.green_800,
- positive_900: color.green_900,
- positive_950: color.green_950,
- positive_975: color.green_975,
-
- negative_25: color.red_25,
- negative_50: color.red_50,
- negative_100: color.red_100,
- negative_200: color.red_200,
- negative_300: color.red_300,
- negative_400: color.red_400,
- negative_500: color.red_500,
- negative_600: color.red_600,
- negative_700: color.red_700,
- negative_800: color.red_800,
- negative_900: color.red_900,
- negative_950: color.red_950,
- negative_975: color.red_975,
- } as const
-
- const darkPalette: Palette = {
- white: color.gray_25,
- black: color.trueBlack,
- like: color.like,
-
- contrast_25: color.gray_975,
- contrast_50: color.gray_950,
- contrast_100: color.gray_900,
- contrast_200: color.gray_800,
- contrast_300: color.gray_700,
- contrast_400: color.gray_600,
- contrast_500: color.gray_500,
- contrast_600: color.gray_400,
- contrast_700: color.gray_300,
- contrast_800: color.gray_200,
- contrast_900: color.gray_100,
- contrast_950: color.gray_50,
- contrast_975: color.gray_25,
-
- primary_25: color.primary_975,
- primary_50: color.primary_950,
- primary_100: color.primary_900,
- primary_200: color.primary_800,
- primary_300: color.primary_700,
- primary_400: color.primary_600,
- primary_500: color.primary_500,
- primary_600: color.primary_400,
- primary_700: color.primary_300,
- primary_800: color.primary_200,
- primary_900: color.primary_100,
- primary_950: color.primary_50,
- primary_975: color.primary_25,
-
- positive_25: color.green_975,
- positive_50: color.green_950,
- positive_100: color.green_900,
- positive_200: color.green_800,
- positive_300: color.green_700,
- positive_400: color.green_600,
- positive_500: color.green_500,
- positive_600: color.green_400,
- positive_700: color.green_300,
- positive_800: color.green_200,
- positive_900: color.green_100,
- positive_950: color.green_50,
- positive_975: color.green_25,
-
- negative_25: color.red_975,
- negative_50: color.red_950,
- negative_100: color.red_900,
- negative_200: color.red_800,
- negative_300: color.red_700,
- negative_400: color.red_600,
- negative_500: color.red_500,
- negative_600: color.red_400,
- negative_700: color.red_300,
- negative_800: color.red_200,
- negative_900: color.red_100,
- negative_950: color.red_50,
- negative_975: color.red_25,
- } as const
-
- const dimPalette: Palette = {
- ...darkPalette,
- black: `hsl(${hues.primary}, 28%, ${dimScale[0]}%)`,
- like: color.like,
-
- contrast_25: `hsl(${hues.primary}, 28%, ${dimScale[1]}%)`,
- contrast_50: `hsl(${hues.primary}, 28%, ${dimScale[2]}%)`,
- contrast_100: `hsl(${hues.primary}, 28%, ${dimScale[3]}%)`,
- contrast_200: `hsl(${hues.primary}, 28%, ${dimScale[4]}%)`,
- contrast_300: `hsl(${hues.primary}, 24%, ${dimScale[5]}%)`,
- contrast_400: `hsl(${hues.primary}, 24%, ${dimScale[6]}%)`,
- contrast_500: `hsl(${hues.primary}, 20%, ${dimScale[7]}%)`,
- contrast_600: `hsl(${hues.primary}, 20%, ${dimScale[8]}%)`,
- contrast_700: `hsl(${hues.primary}, 20%, ${dimScale[9]}%)`,
- contrast_800: `hsl(${hues.primary}, 20%, ${dimScale[10]}%)`,
- contrast_900: `hsl(${hues.primary}, 20%, ${dimScale[11]}%)`,
- contrast_950: `hsl(${hues.primary}, 20%, ${dimScale[12]}%)`,
- contrast_975: `hsl(${hues.primary}, 20%, ${dimScale[13]}%)`,
-
- primary_25: `hsl(${hues.primary}, 50%, ${dimScale[1]}%)`,
- primary_50: `hsl(${hues.primary}, 60%, ${dimScale[2]}%)`,
- primary_100: `hsl(${hues.primary}, 70%, ${dimScale[3]}%)`,
- primary_200: `hsl(${hues.primary}, 82%, ${dimScale[4]}%)`,
- primary_300: `hsl(${hues.primary}, 90%, ${dimScale[5]}%)`,
- primary_400: `hsl(${hues.primary}, 95%, ${dimScale[6]}%)`,
- primary_500: `hsl(${hues.primary}, 99%, ${dimScale[7]}%)`,
- primary_600: `hsl(${hues.primary}, 99%, ${dimScale[8]}%)`,
- primary_700: `hsl(${hues.primary}, 99%, ${dimScale[9]}%)`,
- primary_800: `hsl(${hues.primary}, 99%, ${dimScale[10]}%)`,
- primary_900: `hsl(${hues.primary}, 99%, ${dimScale[11]}%)`,
- primary_950: `hsl(${hues.primary}, 99%, ${dimScale[12]}%)`,
- primary_975: `hsl(${hues.primary}, 99%, ${dimScale[13]}%)`,
-
- positive_25: `hsl(${hues.positive}, 50%, ${dimScale[1]}%)`,
- positive_50: `hsl(${hues.positive}, 60%, ${dimScale[2]}%)`,
- positive_100: `hsl(${hues.positive}, 70%, ${dimScale[3]}%)`,
- positive_200: `hsl(${hues.positive}, 82%, ${dimScale[4]}%)`,
- positive_300: `hsl(${hues.positive}, 82%, ${dimScale[5]}%)`,
- positive_400: `hsl(${hues.positive}, 82%, ${dimScale[6]}%)`,
- positive_500: `hsl(${hues.positive}, 82%, ${dimScale[7]}%)`,
- positive_600: `hsl(${hues.positive}, 82%, ${dimScale[8]}%)`,
- positive_700: `hsl(${hues.positive}, 82%, ${dimScale[9]}%)`,
- positive_800: `hsl(${hues.positive}, 82%, ${dimScale[10]}%)`,
- positive_900: `hsl(${hues.positive}, 82%, ${dimScale[11]}%)`,
- positive_950: `hsl(${hues.positive}, 82%, ${dimScale[12]}%)`,
- positive_975: `hsl(${hues.positive}, 82%, ${dimScale[13]}%)`,
-
- negative_25: `hsl(${hues.negative}, 70%, ${dimScale[1]}%)`,
- negative_50: `hsl(${hues.negative}, 80%, ${dimScale[2]}%)`,
- negative_100: `hsl(${hues.negative}, 84%, ${dimScale[3]}%)`,
- negative_200: `hsl(${hues.negative}, 88%, ${dimScale[4]}%)`,
- negative_300: `hsl(${hues.negative}, 91%, ${dimScale[5]}%)`,
- negative_400: `hsl(${hues.negative}, 91%, ${dimScale[6]}%)`,
- negative_500: `hsl(${hues.negative}, 91%, ${dimScale[7]}%)`,
- negative_600: `hsl(${hues.negative}, 91%, ${dimScale[8]}%)`,
- negative_700: `hsl(${hues.negative}, 91%, ${dimScale[9]}%)`,
- negative_800: `hsl(${hues.negative}, 91%, ${dimScale[10]}%)`,
- negative_900: `hsl(${hues.negative}, 91%, ${dimScale[11]}%)`,
- negative_950: `hsl(${hues.negative}, 91%, ${dimScale[12]}%)`,
- negative_975: `hsl(${hues.negative}, 91%, ${dimScale[13]}%)`,
- } as const
-
- const light: Theme = {
- scheme: 'light',
- name: 'light',
- palette: lightPalette,
- atoms: {
- text: {
- color: lightPalette.black,
- },
- text_contrast_low: {
- color: lightPalette.contrast_400,
- },
- text_contrast_medium: {
- color: lightPalette.contrast_700,
- },
- text_contrast_high: {
- color: lightPalette.contrast_900,
- },
- text_inverted: {
- color: lightPalette.white,
- },
- bg: {
- backgroundColor: lightPalette.white,
- },
- bg_contrast_25: {
- backgroundColor: lightPalette.contrast_25,
- },
- bg_contrast_50: {
- backgroundColor: lightPalette.contrast_50,
- },
- bg_contrast_100: {
- backgroundColor: lightPalette.contrast_100,
- },
- bg_contrast_200: {
- backgroundColor: lightPalette.contrast_200,
- },
- bg_contrast_300: {
- backgroundColor: lightPalette.contrast_300,
- },
- bg_contrast_400: {
- backgroundColor: lightPalette.contrast_400,
- },
- bg_contrast_500: {
- backgroundColor: lightPalette.contrast_500,
- },
- bg_contrast_600: {
- backgroundColor: lightPalette.contrast_600,
- },
- bg_contrast_700: {
- backgroundColor: lightPalette.contrast_700,
- },
- bg_contrast_800: {
- backgroundColor: lightPalette.contrast_800,
- },
- bg_contrast_900: {
- backgroundColor: lightPalette.contrast_900,
- },
- bg_contrast_950: {
- backgroundColor: lightPalette.contrast_950,
- },
- bg_contrast_975: {
- backgroundColor: lightPalette.contrast_975,
- },
- border_contrast_low: {
- borderColor: lightPalette.contrast_100,
- },
- border_contrast_medium: {
- borderColor: lightPalette.contrast_200,
- },
- border_contrast_high: {
- borderColor: lightPalette.contrast_300,
- },
- shadow_sm: {
- ...atoms.shadow_sm,
- shadowColor: lightPalette.black,
- },
- shadow_md: {
- ...atoms.shadow_md,
- shadowColor: lightPalette.black,
- },
- shadow_lg: {
- ...atoms.shadow_lg,
- shadowColor: lightPalette.black,
- },
- },
- }
-
- const dark: Theme = {
- scheme: 'dark',
- name: 'dark',
- palette: darkPalette,
- atoms: {
- text: {
- color: darkPalette.white,
- },
- text_contrast_low: {
- color: darkPalette.contrast_400,
- },
- text_contrast_medium: {
- color: darkPalette.contrast_600,
- },
- text_contrast_high: {
- color: darkPalette.contrast_900,
- },
- text_inverted: {
- color: darkPalette.black,
- },
- bg: {
- backgroundColor: darkPalette.black,
- },
- bg_contrast_25: {
- backgroundColor: darkPalette.contrast_25,
- },
- bg_contrast_50: {
- backgroundColor: darkPalette.contrast_50,
- },
- bg_contrast_100: {
- backgroundColor: darkPalette.contrast_100,
- },
- bg_contrast_200: {
- backgroundColor: darkPalette.contrast_200,
- },
- bg_contrast_300: {
- backgroundColor: darkPalette.contrast_300,
- },
- bg_contrast_400: {
- backgroundColor: darkPalette.contrast_400,
- },
- bg_contrast_500: {
- backgroundColor: darkPalette.contrast_500,
- },
- bg_contrast_600: {
- backgroundColor: darkPalette.contrast_600,
- },
- bg_contrast_700: {
- backgroundColor: darkPalette.contrast_700,
- },
- bg_contrast_800: {
- backgroundColor: darkPalette.contrast_800,
- },
- bg_contrast_900: {
- backgroundColor: darkPalette.contrast_900,
- },
- bg_contrast_950: {
- backgroundColor: darkPalette.contrast_950,
- },
- bg_contrast_975: {
- backgroundColor: darkPalette.contrast_975,
- },
- border_contrast_low: {
- borderColor: darkPalette.contrast_100,
- },
- border_contrast_medium: {
- borderColor: darkPalette.contrast_200,
- },
- border_contrast_high: {
- borderColor: darkPalette.contrast_300,
- },
- shadow_sm: {
- ...atoms.shadow_sm,
- shadowOpacity: 0.7,
- shadowColor: color.trueBlack,
- },
- shadow_md: {
- ...atoms.shadow_md,
- shadowOpacity: 0.7,
- shadowColor: color.trueBlack,
- },
- shadow_lg: {
- ...atoms.shadow_lg,
- shadowOpacity: 0.7,
- shadowColor: color.trueBlack,
- },
- },
- }
-
- const dim: Theme = {
- ...dark,
- scheme: 'dark',
- name: 'dim',
- palette: dimPalette,
- atoms: {
- ...dark.atoms,
- text: {
- color: dimPalette.white,
- },
- text_contrast_low: {
- color: dimPalette.contrast_400,
- },
- text_contrast_medium: {
- color: dimPalette.contrast_600,
- },
- text_contrast_high: {
- color: dimPalette.contrast_900,
- },
- text_inverted: {
- color: dimPalette.black,
- },
- bg: {
- backgroundColor: dimPalette.black,
- },
- bg_contrast_25: {
- backgroundColor: dimPalette.contrast_25,
- },
- bg_contrast_50: {
- backgroundColor: dimPalette.contrast_50,
- },
- bg_contrast_100: {
- backgroundColor: dimPalette.contrast_100,
- },
- bg_contrast_200: {
- backgroundColor: dimPalette.contrast_200,
- },
- bg_contrast_300: {
- backgroundColor: dimPalette.contrast_300,
- },
- bg_contrast_400: {
- backgroundColor: dimPalette.contrast_400,
- },
- bg_contrast_500: {
- backgroundColor: dimPalette.contrast_500,
- },
- bg_contrast_600: {
- backgroundColor: dimPalette.contrast_600,
- },
- bg_contrast_700: {
- backgroundColor: dimPalette.contrast_700,
- },
- bg_contrast_800: {
- backgroundColor: dimPalette.contrast_800,
- },
- bg_contrast_900: {
- backgroundColor: dimPalette.contrast_900,
- },
- bg_contrast_950: {
- backgroundColor: dimPalette.contrast_950,
- },
- bg_contrast_975: {
- backgroundColor: dimPalette.contrast_975,
- },
- border_contrast_low: {
- borderColor: dimPalette.contrast_100,
- },
- border_contrast_medium: {
- borderColor: dimPalette.contrast_200,
- },
- border_contrast_high: {
- borderColor: dimPalette.contrast_300,
- },
- shadow_sm: {
- ...atoms.shadow_sm,
- shadowOpacity: 0.7,
- shadowColor: `hsl(${hues.primary}, 28%, 6%)`,
- },
- shadow_md: {
- ...atoms.shadow_md,
- shadowOpacity: 0.7,
- shadowColor: `hsl(${hues.primary}, 28%, 6%)`,
- },
- shadow_lg: {
- ...atoms.shadow_lg,
- shadowOpacity: 0.7,
- shadowColor: `hsl(${hues.primary}, 28%, 6%)`,
- },
- },
- }
-
- return {
- lightPalette,
- darkPalette,
- dimPalette,
- light,
- dark,
- dim,
- }
+export const themes = {
+ lightPalette: DEFAULT_THEMES.light.palette,
+ darkPalette: DEFAULT_THEMES.dark.palette,
+ dimPalette: DEFAULT_THEMES.dim.palette,
+ light: DEFAULT_THEMES.light,
+ dark: DEFAULT_THEMES.dark,
+ dim: DEFAULT_THEMES.dim,
}
+
+/**
+ * @deprecated use ALF and access palette from `useTheme()`
+ */
+export const lightPalette = DEFAULT_THEMES.light.palette
+/**
+ * @deprecated use ALF and access palette from `useTheme()`
+ */
+export const darkPalette = DEFAULT_THEMES.dark.palette
+/**
+ * @deprecated use ALF and access palette from `useTheme()`
+ */
+export const dimPalette = DEFAULT_THEMES.dim.palette
+/**
+ * @deprecated use ALF and access theme from `useTheme()`
+ */
+export const light = DEFAULT_THEMES.light
+/**
+ * @deprecated use ALF and access theme from `useTheme()`
+ */
+export const dark = DEFAULT_THEMES.dark
+/**
+ * @deprecated use ALF and access theme from `useTheme()`
+ */
+export const dim = DEFAULT_THEMES.dim
diff --git a/src/alf/tokens.ts b/src/alf/tokens.ts
index 74cc160fc6..ac48bc7883 100644
--- a/src/alf/tokens.ts
+++ b/src/alf/tokens.ts
@@ -1,61 +1,10 @@
-import {isAndroid} from '#/platform/detection'
+import {tokens} from '@bsky.app/alf'
-export const TRACKING = isAndroid ? 0.1 : 0
+export * from '@bsky.app/alf/dist/tokens'
export const color = {
- temp_purple: 'rgb(105 0 255)',
- temp_purple_dark: 'rgb(83 0 202)',
-} as const
-
-export const space = {
- _2xs: 2,
- xs: 4,
- sm: 8,
- md: 12,
- lg: 16,
- xl: 20,
- _2xl: 24,
- _3xl: 28,
- _4xl: 32,
- _5xl: 40,
-} as const
-
-export const fontSize = {
- _2xs: 10,
- xs: 12,
- sm: 14,
- md: 16,
- lg: 18,
- xl: 20,
- _2xl: 22,
- _3xl: 26,
- _4xl: 32,
- _5xl: 40,
-} as const
-
-export const lineHeight = {
- none: 1,
- normal: 1.5,
- relaxed: 1.625,
-} as const
-
-export const borderRadius = {
- _2xs: 2,
- xs: 4,
- sm: 8,
- md: 12,
- lg: 16,
- full: 999,
-} as const
-
-/**
- * These correspond to Inter font files we actually load.
- */
-export const fontWeight = {
- normal: '400',
- medium: '500',
- bold: '600',
- heavy: '800',
+ temp_purple: tokens.labelerColor.purple,
+ temp_purple_dark: tokens.labelerColor.purple_dark,
} as const
export const gradients = {
diff --git a/src/alf/types.ts b/src/alf/types.ts
deleted file mode 100644
index d2ac5dbaa3..0000000000
--- a/src/alf/types.ts
+++ /dev/null
@@ -1,164 +0,0 @@
-import {type StyleProp, type TextStyle, type ViewStyle} from 'react-native'
-
-export type TextStyleProp = {
- style?: StyleProp
-}
-
-export type ViewStyleProp = {
- style?: StyleProp
-}
-
-export type ThemeName = 'light' | 'dim' | 'dark'
-export type Palette = {
- white: string
- black: string
- like: string
-
- contrast_25: string
- contrast_50: string
- contrast_100: string
- contrast_200: string
- contrast_300: string
- contrast_400: string
- contrast_500: string
- contrast_600: string
- contrast_700: string
- contrast_800: string
- contrast_900: string
- contrast_950: string
- contrast_975: string
-
- primary_25: string
- primary_50: string
- primary_100: string
- primary_200: string
- primary_300: string
- primary_400: string
- primary_500: string
- primary_600: string
- primary_700: string
- primary_800: string
- primary_900: string
- primary_950: string
- primary_975: string
-
- positive_25: string
- positive_50: string
- positive_100: string
- positive_200: string
- positive_300: string
- positive_400: string
- positive_500: string
- positive_600: string
- positive_700: string
- positive_800: string
- positive_900: string
- positive_950: string
- positive_975: string
-
- negative_25: string
- negative_50: string
- negative_100: string
- negative_200: string
- negative_300: string
- negative_400: string
- negative_500: string
- negative_600: string
- negative_700: string
- negative_800: string
- negative_900: string
- negative_950: string
- negative_975: string
-}
-export type ThemedAtoms = {
- text: {
- color: string
- }
- text_contrast_low: {
- color: string
- }
- text_contrast_medium: {
- color: string
- }
- text_contrast_high: {
- color: string
- }
- text_inverted: {
- color: string
- }
- bg: {
- backgroundColor: string
- }
- bg_contrast_25: {
- backgroundColor: string
- }
- bg_contrast_50: {
- backgroundColor: string
- }
- bg_contrast_100: {
- backgroundColor: string
- }
- bg_contrast_200: {
- backgroundColor: string
- }
- bg_contrast_300: {
- backgroundColor: string
- }
- bg_contrast_400: {
- backgroundColor: string
- }
- bg_contrast_500: {
- backgroundColor: string
- }
- bg_contrast_600: {
- backgroundColor: string
- }
- bg_contrast_700: {
- backgroundColor: string
- }
- bg_contrast_800: {
- backgroundColor: string
- }
- bg_contrast_900: {
- backgroundColor: string
- }
- bg_contrast_950: {
- backgroundColor: string
- }
- bg_contrast_975: {
- backgroundColor: string
- }
- border_contrast_low: {
- borderColor: string
- }
- border_contrast_medium: {
- borderColor: string
- }
- border_contrast_high: {
- borderColor: string
- }
- shadow_sm: {
- shadowRadius: number
- shadowOpacity: number
- elevation: number
- shadowColor: string
- }
- shadow_md: {
- shadowRadius: number
- shadowOpacity: number
- elevation: number
- shadowColor: string
- }
- shadow_lg: {
- shadowRadius: number
- shadowOpacity: number
- elevation: number
- shadowColor: string
- }
-}
-export type Theme = {
- scheme: 'light' | 'dark' // for library support
- name: ThemeName
- palette: Palette
- atoms: ThemedAtoms
-}
diff --git a/src/alf/typography.tsx b/src/alf/typography.tsx
index c229ab4435..3c3bb95489 100644
--- a/src/alf/typography.tsx
+++ b/src/alf/typography.tsx
@@ -8,21 +8,6 @@ import {isNative} from '#/platform/detection'
import {isIOS} from '#/platform/detection'
import {type Alf, applyFonts, atoms, flatten} from '#/alf'
-/**
- * Util to calculate lineHeight from a text size atom and a leading atom
- *
- * Example:
- * `leading(atoms.text_md, atoms.leading_normal)` // => 24
- */
-export function leading<
- Size extends {fontSize?: number},
- Leading extends {lineHeight?: number},
->(textSize: Size, leading: Leading) {
- const size = textSize?.fontSize || atoms.text_md.fontSize
- const lineHeight = leading?.lineHeight || atoms.leading_normal.lineHeight
- return Math.round(size * lineHeight)
-}
-
/**
* Ensures that `lineHeight` defaults to a relative value of `1`, or applies
* other relative leading atoms.
diff --git a/src/alf/util/__tests__/colors.test.ts b/src/alf/util/__tests__/colors.test.ts
index 350b6ff4a4..1ecb7394bd 100644
--- a/src/alf/util/__tests__/colors.test.ts
+++ b/src/alf/util/__tests__/colors.test.ts
@@ -1,12 +1,7 @@
import {jest} from '@jest/globals'
-import {logger} from '#/logger'
import {transparentifyColor} from '../colorGeneration'
-jest.mock('#/logger', () => ({
- logger: {warn: jest.fn()},
-}))
-
describe('transparentifyColor', () => {
beforeEach(() => {
jest.clearAllMocks()
@@ -41,8 +36,5 @@ describe('transparentifyColor', () => {
const unsupported = 'blue'
const result = transparentifyColor(unsupported, 0.5)
expect(result).toBe(unsupported)
- expect(logger.warn).toHaveBeenCalledWith(
- `Could not make '${unsupported}' transparent`,
- )
})
})
diff --git a/src/alf/util/colorGeneration.ts b/src/alf/util/colorGeneration.ts
index 574ab0a496..467270676a 100644
--- a/src/alf/util/colorGeneration.ts
+++ b/src/alf/util/colorGeneration.ts
@@ -1,49 +1,8 @@
-import {logger} from '#/logger'
+import {utils} from '@bsky.app/alf'
export const BLUE_HUE = 211
-export const RED_HUE = 346
-export const GREEN_HUE = 152
/**
- * Smooth progression of lightness "stops" for generating HSL colors.
+ * @deprecated use `utils.alpha` from `@bsky.app/alf` instead
*/
-export const COLOR_STOPS = [
- 0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.85, 0.9, 0.95, 1,
-]
-
-export function generateScale(start: number, end: number) {
- const range = end - start
- return COLOR_STOPS.map(stop => {
- return start + range * stop
- })
-}
-
-export const defaultScale = generateScale(6, 100)
-// dim shifted 6% lighter
-export const dimScale = generateScale(12, 100)
-
-export function transparentifyColor(color: string, alpha: number) {
- if (color.startsWith('hsl(')) {
- return 'hsla(' + color.slice('hsl('.length, -1) + `, ${alpha})`
- } else if (color.startsWith('rgb(')) {
- return 'rgba(' + color.slice('rgb('.length, -1) + `, ${alpha})`
- } else if (color.startsWith('#')) {
- if (color.length === 7) {
- const alphaHex = Math.round(alpha * 255).toString(16)
- // Per MDN: If there is only one number, it is duplicated: e means ee
- // https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color
- return color.slice(0, 7) + alphaHex.padStart(2, alphaHex)
- } else if (color.length === 4) {
- // convert to 6-digit hex before adding alpha
- const [r, g, b] = color.slice(1).split('')
- const alphaHex = Math.round(alpha * 255).toString(16)
- return `#${r.repeat(2)}${g.repeat(2)}${b.repeat(2)}${alphaHex.padStart(
- 2,
- alphaHex,
- )}`
- }
- } else {
- logger.warn(`Could not make '${color}' transparent`)
- }
- return color
-}
+export const transparentifyColor = utils.alpha
diff --git a/src/alf/util/platform.ts b/src/alf/util/platform.ts
index 947f2bd163..2f66c76c1a 100644
--- a/src/alf/util/platform.ts
+++ b/src/alf/util/platform.ts
@@ -1,62 +1 @@
-import {Platform} from 'react-native'
-
-import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection'
-
-/**
- * Identity function on web. Returns nothing on other platforms.
- *
- * Note: Platform splitting does not tree-shake away the other platforms,
- * so don't do stuff like e.g. rely on platform-specific imports. Use
- * platform-split files instead.
- */
-export function web(value: any) {
- if (isWeb) {
- return value
- }
-}
-
-/**
- * Identity function on iOS. Returns nothing on other platforms.
- *
- * Note: Platform splitting does not tree-shake away the other platforms,
- * so don't do stuff like e.g. rely on platform-specific imports. Use
- * platform-split files instead.
- */
-export function ios(value: any) {
- if (isIOS) {
- return value
- }
-}
-
-/**
- * Identity function on Android. Returns nothing on other platforms..
- *
- * Note: Platform splitting does not tree-shake away the other platforms,
- * so don't do stuff like e.g. rely on platform-specific imports. Use
- * platform-split files instead.
- */
-export function android(value: any) {
- if (isAndroid) {
- return value
- }
-}
-
-/**
- * Identity function on iOS and Android. Returns nothing on web.
- *
- * Note: Platform splitting does not tree-shake away the other platforms,
- * so don't do stuff like e.g. rely on platform-specific imports. Use
- * platform-split files instead.
- */
-export function native(value: any) {
- if (isNative) {
- return value
- }
-}
-
-/**
- * Note: Platform splitting does not tree-shake away the other platforms,
- * so don't do stuff like e.g. rely on platform-specific imports. Use
- * platform-split files instead.
- */
-export const platform = Platform.select
+export {android, ios, native, platform, web} from '@bsky.app/alf'
diff --git a/src/alf/util/systemUI.ts b/src/alf/util/systemUI.ts
index 9e5769c4c6..d013f0c3f9 100644
--- a/src/alf/util/systemUI.ts
+++ b/src/alf/util/systemUI.ts
@@ -1,14 +1,20 @@
import * as SystemUI from 'expo-system-ui'
+import {type Theme} from '@bsky.app/alf'
+import {logger} from '#/logger'
import {isAndroid} from '#/platform/detection'
-import {type Theme} from '../types'
export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) {
if (isAndroid) {
- if (themeType === 'theme') {
- SystemUI.setBackgroundColorAsync(t.atoms.bg.backgroundColor)
- } else {
- SystemUI.setBackgroundColorAsync('black')
+ try {
+ if (themeType === 'theme') {
+ SystemUI.setBackgroundColorAsync(t.atoms.bg.backgroundColor)
+ } else {
+ SystemUI.setBackgroundColorAsync('black')
+ }
+ } catch (error) {
+ // Can reject with 'The current activity is no longer available' - no big deal
+ logger.debug('Could not set system UI theme', {safeMessage: error})
}
}
}
diff --git a/src/alf/util/themeSelector.ts b/src/alf/util/themeSelector.ts
index c118b65069..b0827342a2 100644
--- a/src/alf/util/themeSelector.ts
+++ b/src/alf/util/themeSelector.ts
@@ -1,14 +1,3 @@
-import {type ThemeName} from '#/alf/types'
+import {utils} from '@bsky.app/alf'
-export function select(name: ThemeName, options: Record) {
- switch (name) {
- case 'light':
- return options.light
- case 'dark':
- return options.dark || options.dim
- case 'dim':
- return options.dim || options.dark
- default:
- throw new Error(`select(theme, options) received unknown theme ${name}`)
- }
-}
+export const select = utils.select
diff --git a/src/alf/util/useColorModeTheme.ts b/src/alf/util/useColorModeTheme.ts
index b0b4f9e7c1..4ae94899cd 100644
--- a/src/alf/util/useColorModeTheme.ts
+++ b/src/alf/util/useColorModeTheme.ts
@@ -1,10 +1,10 @@
import React from 'react'
import {type ColorSchemeName, useColorScheme} from 'react-native'
+import {type ThemeName} from '@bsky.app/alf'
import {isWeb} from '#/platform/detection'
import {useThemePrefs} from '#/state/shell'
import {dark, dim, light} from '#/alf/themes'
-import {type ThemeName} from '#/alf/types'
export function useColorModeTheme(): ThemeName {
const theme = useThemeName()
diff --git a/src/components/AccountList.tsx b/src/components/AccountList.tsx
index eb770eeca7..e3b2b7d129 100644
--- a/src/components/AccountList.tsx
+++ b/src/components/AccountList.tsx
@@ -77,7 +77,7 @@ export function AccountList({
]}>
{sanitizeDisplayName(
profile?.displayName || profile?.handle || account.handle,
diff --git a/src/components/Admonition.tsx b/src/components/Admonition.tsx
index ea6751955f..961d94b37e 100644
--- a/src/components/Admonition.tsx
+++ b/src/components/Admonition.tsx
@@ -3,17 +3,13 @@ import {type StyleProp, View, type ViewStyle} from 'react-native'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button as BaseButton, type ButtonProps} from '#/components/Button'
-import {CircleInfo_Stroke2_Corner0_Rounded as ErrorIcon} from '#/components/icons/CircleInfo'
-import {Eye_Stroke2_Corner0_Rounded as InfoIcon} from '#/components/icons/Eye'
-import {Leaf_Stroke2_Corner0_Rounded as TipIcon} from '#/components/icons/Leaf'
+import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
+import {CircleX_Stroke2_Corner0_Rounded as CircleXIcon} from '#/components/icons/CircleX'
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
import {Text as BaseText, type TextProps} from '#/components/Typography'
export const colors = {
- warning: {
- light: '#DFBC00',
- dark: '#BFAF1F',
- },
+ warning: '#FFC404',
}
type Context = {
@@ -29,29 +25,44 @@ export function Icon() {
const t = useTheme()
const {type} = useContext(Context)
const Icon = {
- info: InfoIcon,
- tip: TipIcon,
+ info: CircleInfoIcon,
+ tip: CircleInfoIcon,
warning: WarningIcon,
- error: ErrorIcon,
+ error: CircleXIcon,
}[type]
const fill = {
info: t.atoms.text_contrast_medium.color,
tip: t.palette.primary_500,
- warning: colors.warning.light,
+ warning: colors.warning,
error: t.palette.negative_500,
}[type]
return
}
+export function Content({
+ children,
+ style,
+ ...rest
+}: {
+ children: React.ReactNode
+ style?: StyleProp
+}) {
+ return (
+
+ {children}
+
+ )
+}
+
export function Text({
children,
style,
...rest
}: Pick) {
return (
-
+
{children}
)
@@ -60,17 +71,23 @@ export function Text({
export function Button({
children,
...props
-}: Omit) {
+}: Omit) {
return (
-
+
{children}
)
}
-export function Row({children}: {children: React.ReactNode}) {
+export function Row({
+ children,
+ style,
+}: {
+ children: React.ReactNode
+ style?: StyleProp
+}) {
return (
-
+
{children}
)
@@ -88,19 +105,20 @@ export function Outer({
const t = useTheme()
const {gtMobile} = useBreakpoints()
const borderColor = {
- info: t.atoms.border_contrast_low.borderColor,
- tip: t.atoms.border_contrast_low.borderColor,
- warning: t.atoms.border_contrast_low.borderColor,
- error: t.atoms.border_contrast_low.borderColor,
+ info: t.atoms.border_contrast_high.borderColor,
+ tip: t.palette.primary_500,
+ warning: colors.warning,
+ error: t.palette.negative_500,
}[type]
return (
@@ -123,7 +141,9 @@ export function Admonition({
- {children}
+
+ {children}
+
)
diff --git a/src/components/BlockedGeoOverlay.tsx b/src/components/BlockedGeoOverlay.tsx
index 8dd55c2bf5..d6e2626875 100644
--- a/src/components/BlockedGeoOverlay.tsx
+++ b/src/components/BlockedGeoOverlay.tsx
@@ -96,7 +96,7 @@ export function BlockedGeoOverlay() {
-
+
Not in Mississippi?
(
}
} else if (color === 'secondary') {
if (!disabled) {
- baseStyles.push(t.atoms.bg_contrast_25)
+ baseStyles.push(t.atoms.bg_contrast_50)
hoverStyles.push(t.atoms.bg_contrast_100)
} else {
baseStyles.push(t.atoms.bg_contrast_50)
@@ -274,51 +274,27 @@ export const Button = React.forwardRef(
} else if (color === 'primary_subtle') {
if (!disabled) {
baseStyles.push({
- backgroundColor: select(t.name, {
- light: t.palette.primary_50,
- dim: t.palette.primary_100,
- dark: t.palette.primary_100,
- }),
+ backgroundColor: t.palette.primary_50,
})
hoverStyles.push({
- backgroundColor: select(t.name, {
- light: t.palette.primary_100,
- dim: t.palette.primary_200,
- dark: t.palette.primary_200,
- }),
+ backgroundColor: t.palette.primary_100,
})
} else {
baseStyles.push({
- backgroundColor: select(t.name, {
- light: t.palette.primary_25,
- dim: t.palette.primary_50,
- dark: t.palette.primary_50,
- }),
+ backgroundColor: t.palette.primary_50,
})
}
} else if (color === 'negative_subtle') {
if (!disabled) {
baseStyles.push({
- backgroundColor: select(t.name, {
- light: t.palette.negative_50,
- dim: t.palette.negative_100,
- dark: t.palette.negative_100,
- }),
+ backgroundColor: t.palette.negative_50,
})
hoverStyles.push({
- backgroundColor: select(t.name, {
- light: t.palette.negative_100,
- dim: t.palette.negative_200,
- dark: t.palette.negative_200,
- }),
+ backgroundColor: t.palette.negative_100,
})
} else {
baseStyles.push({
- backgroundColor: select(t.name, {
- light: t.palette.negative_25,
- dim: t.palette.negative_50,
- dark: t.palette.negative_50,
- }),
+ backgroundColor: t.palette.negative_50,
})
}
}
@@ -372,7 +348,7 @@ export const Button = React.forwardRef(
if (!disabled) {
baseStyles.push(t.atoms.bg)
hoverStyles.push({
- backgroundColor: t.palette.contrast_25,
+ backgroundColor: t.palette.contrast_50,
})
}
}
@@ -396,7 +372,7 @@ export const Button = React.forwardRef(
if (!disabled) {
baseStyles.push(t.atoms.bg)
hoverStyles.push({
- backgroundColor: t.palette.contrast_25,
+ backgroundColor: t.palette.contrast_50,
})
}
}
@@ -626,37 +602,21 @@ export function useSharedButtonTextStyles() {
} else if (color === 'primary_subtle') {
if (!disabled) {
baseStyles.push({
- color: select(t.name, {
- light: t.palette.primary_600,
- dim: t.palette.primary_800,
- dark: t.palette.primary_800,
- }),
+ color: t.palette.primary_600,
})
} else {
baseStyles.push({
- color: select(t.name, {
- light: t.palette.primary_200,
- dim: t.palette.primary_200,
- dark: t.palette.primary_200,
- }),
+ color: t.palette.primary_200,
})
}
} else if (color === 'negative_subtle') {
if (!disabled) {
baseStyles.push({
- color: select(t.name, {
- light: t.palette.negative_600,
- dim: t.palette.negative_800,
- dark: t.palette.negative_800,
- }),
+ color: t.palette.negative_600,
})
} else {
baseStyles.push({
- color: select(t.name, {
- light: t.palette.negative_200,
- dim: t.palette.negative_200,
- dark: t.palette.negative_200,
- }),
+ color: t.palette.negative_200,
})
}
}
@@ -763,7 +723,7 @@ export function useSharedButtonTextStyles() {
} else if (size === 'small') {
baseStyles.push(a.text_sm, a.leading_snug, a.font_medium)
} else if (size === 'tiny') {
- baseStyles.push(a.text_xs, a.leading_snug, a.font_medium)
+ baseStyles.push(a.text_xs, a.leading_snug, a.font_semi_bold)
}
return StyleSheet.flatten(baseStyles)
@@ -877,3 +837,47 @@ export function ButtonIcon({
)
}
+
+export type StackedButtonProps = Omit<
+ ButtonProps,
+ keyof VariantProps | 'children'
+> &
+ Pick & {
+ children: React.ReactNode
+ icon: React.ComponentType
+ }
+
+export function StackedButton({children, ...props}: StackedButtonProps) {
+ return (
+
+ )
+}
+
+function StackedButtonInnerText({
+ children,
+ icon: Icon,
+}: Pick) {
+ const textStyles = useSharedButtonTextStyles()
+ return (
+ <>
+
+ {children}
+ >
+ )
+}
diff --git a/src/components/ContextMenu/index.tsx b/src/components/ContextMenu/index.tsx
index 9d7189473c..7d078bb2de 100644
--- a/src/components/ContextMenu/index.tsx
+++ b/src/components/ContextMenu/index.tsx
@@ -796,7 +796,7 @@ export function ItemText({children, style}: ItemTextProps) {
style={[
a.flex_1,
a.text_md,
- a.font_bold,
+ a.font_semi_bold,
t.atoms.text_contrast_high,
{paddingTop: 3},
style,
@@ -855,7 +855,11 @@ export function LabelText({children}: {children: React.ReactNode}) {
const t = useTheme()
return (
+ style={[
+ a.font_semi_bold,
+ t.atoms.text_contrast_medium,
+ {marginBottom: -8},
+ ]}>
{children}
)
diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx
index de8287a53c..ee2c0f76c7 100644
--- a/src/components/Dialog/index.tsx
+++ b/src/components/Dialog/index.tsx
@@ -267,7 +267,10 @@ export const ScrollableInner = React.forwardRef(
scrollEventThrottle={50}
onScroll={isAndroid ? onScroll : undefined}
keyboardShouldPersistTaps="handled"
- stickyHeaderIndices={header ? [0] : undefined}>
+ // TODO: figure out why this positions the header absolutely (rather than stickily)
+ // on Android. fine to disable for now, because we don't have any
+ // dialogs that use this that actually scroll -sfn
+ stickyHeaderIndices={ios(header ? [0] : undefined)}>
{header}
{children}
diff --git a/src/components/Dialog/shared.tsx b/src/components/Dialog/shared.tsx
index b5513b19c4..e9fdf50fa7 100644
--- a/src/components/Dialog/shared.tsx
+++ b/src/components/Dialog/shared.tsx
@@ -62,7 +62,7 @@ export function HeaderText({
style?: StyleProp
}) {
return (
-
+
{children}
)
diff --git a/src/components/Error.tsx b/src/components/Error.tsx
index dc8e53b46e..92b8b2e7cd 100644
--- a/src/components/Error.tsx
+++ b/src/components/Error.tsx
@@ -40,7 +40,7 @@ export function Error({
]}
sideBorders={sideBorders}>
- {title}
+ {title}
{title}
@@ -214,7 +214,7 @@ export function DescriptionPlaceholder() {
export function Likes({count}: {count: number}) {
const t = useTheme()
return (
-
+
Liked by
diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx
index 6278449a07..5a65215b12 100644
--- a/src/components/FeedInterstitials.tsx
+++ b/src/components/FeedInterstitials.tsx
@@ -380,7 +380,7 @@ export function ProfileGrid({
a.justify_between,
]}
pointerEvents={isIOS ? 'auto' : 'box-none'}>
-
+
{isFeedContext ? (
Suggested for you
) : (
@@ -516,7 +516,7 @@ export function SuggestedFeeds() {
style={[
a.flex_1,
a.text_lg,
- a.font_bold,
+ a.font_semi_bold,
t.atoms.text_contrast_medium,
]}>
Some other feeds you might like
diff --git a/src/components/LabelingServiceCard/index.tsx b/src/components/LabelingServiceCard/index.tsx
index 990dbb1c69..ecb47a7588 100644
--- a/src/components/LabelingServiceCard/index.tsx
+++ b/src/components/LabelingServiceCard/index.tsx
@@ -45,7 +45,7 @@ export function Avatar({avatar}: {avatar?: string}) {
export function Title({value}: {value: string}) {
return (
-
+
{value}
)
diff --git a/src/components/Layout/Header/index.tsx b/src/components/Layout/Header/index.tsx
index 1a049a6966..596540a23e 100644
--- a/src/components/Layout/Header/index.tsx
+++ b/src/components/Layout/Header/index.tsx
@@ -181,7 +181,7 @@ export function TitleText({
Hidden list
@@ -135,7 +135,7 @@ export function TitleAndByline({
{title}
diff --git a/src/components/LoggedOutCTA.tsx b/src/components/LoggedOutCTA.tsx
index 0bafbd45f1..de0e23dc96 100644
--- a/src/components/LoggedOutCTA.tsx
+++ b/src/components/LoggedOutCTA.tsx
@@ -50,7 +50,7 @@ export function LoggedOutCTA({style, gateName}: LoggedOutCTAProps) {
-
+
Join Bluesky
-
+
{isMe ? Welcome, friend! : Say hello!}
diff --git a/src/components/Pills.tsx b/src/components/Pills.tsx
index 550089a809..4469c83bd6 100644
--- a/src/components/Pills.tsx
+++ b/src/components/Pills.tsx
@@ -133,7 +133,7 @@ export function Label({
emoji
style={[
text,
- a.font_bold,
+ a.font_semi_bold,
a.leading_tight,
t.atoms.text_contrast_medium,
{paddingRight: 3},
diff --git a/src/components/PolicyUpdateOverlay/Badge.tsx b/src/components/PolicyUpdateOverlay/Badge.tsx
index 3829f60a5a..b3bb8b77b2 100644
--- a/src/components/PolicyUpdateOverlay/Badge.tsx
+++ b/src/components/PolicyUpdateOverlay/Badge.tsx
@@ -25,7 +25,7 @@ export function Badge() {
-
+
Hey there 👋
@@ -115,7 +115,7 @@ export function Content({state}: {state: PolicyUpdateState}) {
) : (
-
+
Hey there 👋
diff --git a/src/components/Post/Embed/ExternalEmbed/index.tsx b/src/components/Post/Embed/ExternalEmbed/index.tsx
index 714eaecd63..7911df726b 100644
--- a/src/components/Post/Embed/ExternalEmbed/index.tsx
+++ b/src/components/Post/Embed/ExternalEmbed/index.tsx
@@ -126,7 +126,7 @@ export const ExternalEmbed = ({
+ style={[a.text_md, a.font_semi_bold, a.leading_snug]}>
{link.title || link.uri}
)}
diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/TimeIndicator.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/TimeIndicator.tsx
index 67af7618c3..01828011b2 100644
--- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/TimeIndicator.tsx
+++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/TimeIndicator.tsx
@@ -54,7 +54,7 @@ export function TimeIndicator({
{`${minutes}:${seconds}`}
diff --git a/src/components/PostControls/PostControlButton.tsx b/src/components/PostControls/PostControlButton.tsx
index f7070c4c8c..9a24eb9173 100644
--- a/src/components/PostControls/PostControlButton.tsx
+++ b/src/components/PostControls/PostControlButton.tsx
@@ -128,7 +128,12 @@ export function PostControlButtonText({style, ...props}: TextProps) {
return (
)
diff --git a/src/components/PostControls/RepostButton.tsx b/src/components/PostControls/RepostButton.tsx
index d4a3960a7f..f34e37c29c 100644
--- a/src/components/PostControls/RepostButton.tsx
+++ b/src/components/PostControls/RepostButton.tsx
@@ -157,7 +157,7 @@ let RepostButtonDialogInner = ({
variant="ghost"
color="primary">
-
+
{isReposted ? (
Remove repost
) : (
@@ -188,7 +188,7 @@ let RepostButtonDialogInner = ({
/>
diff --git a/src/components/PostControls/ShareMenu/RecentChats.tsx b/src/components/PostControls/ShareMenu/RecentChats.tsx
index 4b94e0df98..28724061fd 100644
--- a/src/components/PostControls/ShareMenu/RecentChats.tsx
+++ b/src/components/PostControls/ShareMenu/RecentChats.tsx
@@ -203,7 +203,7 @@ function NoConvos() {
a.text_sm,
t.atoms.text_contrast_high,
a.text_center,
- a.font_bold,
+ a.font_semi_bold,
]}>
Start a conversation, and it will appear here.
diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx
index 095b621679..70323f9fca 100644
--- a/src/components/ProfileCard.tsx
+++ b/src/components/ProfileCard.tsx
@@ -233,7 +233,7 @@ function InlineNameAndHandle({
+ style={[a.text_lg, a.font_semi_bold, a.self_start]}>
{sanitizeDisplayName(
profile.displayName || sanitizeHandle(profile.handle),
moderation.ui('displayName'),
@@ -562,7 +562,7 @@ function Inner({
label={`${followers} ${pluralizedFollowers}`}
style={[t.atoms.text]}
onPress={hide}>
- {followers}
+ {followers}
{pluralizedFollowers}
@@ -572,7 +572,7 @@ function Inner({
label={_(msg`${following} following`)}
style={[t.atoms.text]}
onPress={hide}>
- {following}
+ {following}
{pluralizedFollowings}
diff --git a/src/components/ProgressGuide/FollowDialog.tsx b/src/components/ProgressGuide/FollowDialog.tsx
index c4a5f0fa02..3dd308e83e 100644
--- a/src/components/ProgressGuide/FollowDialog.tsx
+++ b/src/components/ProgressGuide/FollowDialog.tsx
@@ -360,7 +360,7 @@ function HeaderTop({guide}: {guide: Follow10ProgressGuide}) {
style={[
a.z_10,
a.text_lg,
- a.font_heavy,
+ a.font_bold,
a.leading_tight,
t.atoms.text_contrast_high,
]}>
diff --git a/src/components/ProgressGuide/List.tsx b/src/components/ProgressGuide/List.tsx
index 52fc4c27a9..cae307a6dd 100644
--- a/src/components/ProgressGuide/List.tsx
+++ b/src/components/ProgressGuide/List.tsx
@@ -28,7 +28,7 @@ export function ProgressGuideList({style}: {style?: StyleProp}) {
diff --git a/src/components/ProgressGuide/Task.tsx b/src/components/ProgressGuide/Task.tsx
index b9ba3fd9ab..449a28fcd3 100644
--- a/src/components/ProgressGuide/Task.tsx
+++ b/src/components/ProgressGuide/Task.tsx
@@ -39,7 +39,7 @@ export function ProgressGuideTask({
diff --git a/src/components/ProgressGuide/Toast.tsx b/src/components/ProgressGuide/Toast.tsx
index d4ac771b25..ad0feff2ec 100644
--- a/src/components/ProgressGuide/Toast.tsx
+++ b/src/components/ProgressGuide/Toast.tsx
@@ -160,7 +160,7 @@ export const ProgressGuideToast = React.forwardRef<
ref={animatedCheckRef}
/>
- {title}
+ {title}
{subtitle && (
{subtitle}
diff --git a/src/components/Prompt.tsx b/src/components/Prompt.tsx
index 626d8316d3..a7bd895219 100644
--- a/src/components/Prompt.tsx
+++ b/src/components/Prompt.tsx
@@ -77,7 +77,7 @@ export function TitleText({
style={[
a.flex_1,
a.text_2xl,
- a.font_bold,
+ a.font_semi_bold,
a.pb_sm,
a.leading_snug,
style,
diff --git a/src/components/ReportDialog/SelectLabelerView.tsx b/src/components/ReportDialog/SelectLabelerView.tsx
index be666ced15..02b3244428 100644
--- a/src/components/ReportDialog/SelectLabelerView.tsx
+++ b/src/components/ReportDialog/SelectLabelerView.tsx
@@ -24,7 +24,7 @@ export function SelectLabelerView({
return (
-
+
Select moderator
@@ -75,7 +75,8 @@ function LabelerButton({
handle: labeler.creator.handle,
})}
/>
-
+
@{labeler.creator.handle}
diff --git a/src/components/ReportDialog/SelectReportOptionView.tsx b/src/components/ReportDialog/SelectReportOptionView.tsx
index f5165f8f74..0fd321cdf8 100644
--- a/src/components/ReportDialog/SelectReportOptionView.tsx
+++ b/src/components/ReportDialog/SelectReportOptionView.tsx
@@ -85,7 +85,7 @@ export function SelectReportOptionView(props: {
) : null}
- {i18n.title}
+ {i18n.title}
{i18n.description}
@@ -173,7 +173,8 @@ function ReportOptionButton({
interacted && t.atoms.bg_contrast_50,
]}>
-
+
{title}
{description}
diff --git a/src/components/ReportDialog/SubmitView.tsx b/src/components/ReportDialog/SubmitView.tsx
index 418f364a7e..3dbb1389d2 100644
--- a/src/components/ReportDialog/SubmitView.tsx
+++ b/src/components/ReportDialog/SubmitView.tsx
@@ -125,7 +125,7 @@ export function SubmitView({
t.atoms.border_contrast_low,
]}>
-
+
{selectedReportOption.title}
diff --git a/src/components/StarterPack/Wizard/ScreenTransition.tsx b/src/components/ScreenTransition.tsx
similarity index 51%
rename from src/components/StarterPack/Wizard/ScreenTransition.tsx
rename to src/components/ScreenTransition.tsx
index c02888e1d1..8c4e7e01f5 100644
--- a/src/components/StarterPack/Wizard/ScreenTransition.tsx
+++ b/src/components/ScreenTransition.tsx
@@ -1,5 +1,6 @@
import {type StyleProp, type ViewStyle} from 'react-native'
import Animated, {
+ Easing,
FadeIn,
FadeOut,
SlideInLeft,
@@ -13,17 +14,25 @@ export function ScreenTransition({
direction,
style,
children,
+ enabledWeb,
}: {
direction: 'Backward' | 'Forward'
style?: StyleProp
children: React.ReactNode
+ enabledWeb?: boolean
}) {
- const entering = direction === 'Forward' ? SlideInRight : SlideInLeft
+ const entering =
+ direction === 'Forward'
+ ? SlideInRight.easing(Easing.out(Easing.exp))
+ : SlideInLeft.easing(Easing.out(Easing.exp))
+ const webEntering = enabledWeb ? FadeIn.duration(90) : undefined
+ const exiting = FadeOut.duration(90) // Totally vibes based
+ const webExiting = enabledWeb ? FadeOut.duration(90) : undefined
return (
{children}
diff --git a/src/components/SearchError.tsx b/src/components/SearchError.tsx
index 443bbab8f8..d478ac8850 100644
--- a/src/components/SearchError.tsx
+++ b/src/components/SearchError.tsx
@@ -34,7 +34,13 @@ export function SearchError({
{maxWidth: gtMobile ? 394 : 294},
gtMobile ? a.gap_md : a.gap_sm,
]}>
-
+
{title}
{children}
diff --git a/src/components/Select/index.tsx b/src/components/Select/index.tsx
index 82520f12a2..33a828911e 100644
--- a/src/components/Select/index.tsx
+++ b/src/components/Select/index.tsx
@@ -280,7 +280,7 @@ export function ItemText({children}: ItemTextProps) {
// eslint-disable-next-line bsky-internal/avoid-unwrapped-text
return (
- {children}
+ {children}
)
}
diff --git a/src/components/Select/index.web.tsx b/src/components/Select/index.web.tsx
index 4e92d3c517..f53749ef0d 100644
--- a/src/components/Select/index.web.tsx
+++ b/src/components/Select/index.web.tsx
@@ -259,7 +259,7 @@ export function Item({ref, value, style, children}: ItemProps) {
a.text_sm,
{outline: 0},
(hovered || focused) && {backgroundColor: t.palette.primary_50},
- selected && [a.font_bold],
+ selected && [a.font_semi_bold],
a.transition_color,
style,
])}>
diff --git a/src/components/StarterPack/ProfileStarterPacks.tsx b/src/components/StarterPack/ProfileStarterPacks.tsx
index 7252a11627..ecb5357e0e 100644
--- a/src/components/StarterPack/ProfileStarterPacks.tsx
+++ b/src/components/StarterPack/ProfileStarterPacks.tsx
@@ -255,7 +255,7 @@ function Empty() {
{marginTop: a.border.borderWidth},
]}>
-
+
You haven't created a starter pack yet!
diff --git a/src/components/StarterPack/QrCode.tsx b/src/components/StarterPack/QrCode.tsx
index 4c28a41c52..64c5cf50f7 100644
--- a/src/components/StarterPack/QrCode.tsx
+++ b/src/components/StarterPack/QrCode.tsx
@@ -54,14 +54,19 @@ export function QrCode({
]}>
+ style={[
+ a.font_semi_bold,
+ a.text_3xl,
+ a.text_center,
+ {color: 'white'},
+ ]}>
{record.name}
@@ -76,7 +81,7 @@ export function QrCode({
a.flex,
a.flex_row,
a.align_center,
- a.font_bold,
+ a.font_semi_bold,
{color: 'white', fontSize: 18, gap: 6},
]}>
diff --git a/src/components/StarterPack/ShareDialog.tsx b/src/components/StarterPack/ShareDialog.tsx
index 32932fe2de..6c282f117b 100644
--- a/src/components/StarterPack/ShareDialog.tsx
+++ b/src/components/StarterPack/ShareDialog.tsx
@@ -77,7 +77,7 @@ function ShareDialogInner({
) : (
-
+
Invite people to this starter pack!
diff --git a/src/components/StarterPack/StarterPackCard.tsx b/src/components/StarterPack/StarterPackCard.tsx
index d889b2e725..e58c2ed271 100644
--- a/src/components/StarterPack/StarterPackCard.tsx
+++ b/src/components/StarterPack/StarterPackCard.tsx
@@ -77,7 +77,7 @@ export function Card({
{record.name}
@@ -97,7 +97,7 @@ export function Card({
) : null}
{!!joinedAllTimeCount && joinedAllTimeCount >= 50 && (
-
+
joined!
diff --git a/src/components/StarterPack/Wizard/WizardEditListDialog.tsx b/src/components/StarterPack/Wizard/WizardEditListDialog.tsx
index 7dfde900f8..b1103bb4c9 100644
--- a/src/components/StarterPack/Wizard/WizardEditListDialog.tsx
+++ b/src/components/StarterPack/Wizard/WizardEditListDialog.tsx
@@ -105,7 +105,7 @@ export function WizardEditListDialog({
: [a.pb_sm, a.align_end],
]}>
-
+
{state.currentStep === 'Profiles' ? (
Edit People
) : (
diff --git a/src/components/StarterPack/Wizard/WizardListCard.tsx b/src/components/StarterPack/Wizard/WizardListCard.tsx
index 09c265d780..4b74311577 100644
--- a/src/components/StarterPack/Wizard/WizardListCard.tsx
+++ b/src/components/StarterPack/Wizard/WizardListCard.tsx
@@ -83,7 +83,7 @@ function WizardListCard({
emoji
style={[
a.flex_1,
- a.font_bold,
+ a.font_semi_bold,
a.text_md,
a.leading_tight,
a.self_start,
diff --git a/src/components/SubtleWebHover.web.tsx b/src/components/SubtleWebHover.web.tsx
index af00cf43a0..8e7b48f93e 100644
--- a/src/components/SubtleWebHover.web.tsx
+++ b/src/components/SubtleWebHover.web.tsx
@@ -11,7 +11,7 @@ export function SubtleWebHover({
if (isTouchDevice) {
return null
}
- let opacity: number
+ let opacity = 0.5
switch (t.name) {
case 'dark':
opacity = 0.4
diff --git a/src/components/TrendingTopics.tsx b/src/components/TrendingTopics.tsx
index 50e0fc5ed2..b2ee8140e9 100644
--- a/src/components/TrendingTopics.tsx
+++ b/src/components/TrendingTopics.tsx
@@ -90,7 +90,7 @@ export function TrendingTopic({
0 && (
-
+
{formatCount(i18n, likeCount)}
@@ -235,7 +236,8 @@ export function VideoPostCard({
{repostCount > 0 && (
-
+
{formatCount(i18n, repostCount)}
@@ -520,7 +522,11 @@ export function CompactVideoPostCard({
+ style={[
+ a.text_sm,
+ a.font_semi_bold,
+ {color: 'white'},
+ ]}>
{formatCount(i18n, likeCount)}
diff --git a/src/components/WelcomeModal.tsx b/src/components/WelcomeModal.tsx
index 7c9ecd84de..1271a5f3ac 100644
--- a/src/components/WelcomeModal.tsx
+++ b/src/components/WelcomeModal.tsx
@@ -108,7 +108,7 @@ export function WelcomeModal({control}: WelcomeModalProps) {
@@ -127,7 +127,7 @@ export function WelcomeModal({control}: WelcomeModalProps) {
-
+
Who can interact with this post?
-
+
Keep me posted
diff --git a/src/components/ageAssurance/AgeAssuranceAdmonition.tsx b/src/components/ageAssurance/AgeAssuranceAdmonition.tsx
index e53c37b345..028e1dad52 100644
--- a/src/components/ageAssurance/AgeAssuranceAdmonition.tsx
+++ b/src/components/ageAssurance/AgeAssuranceAdmonition.tsx
@@ -82,13 +82,13 @@ function Inner({
{children}
-
+
Learn more in your{' '}
{
logger.metric('ageAssurance:navigateToSettings', {})
}}>
diff --git a/src/components/ageAssurance/AgeAssuranceAppealDialog.tsx b/src/components/ageAssurance/AgeAssuranceAppealDialog.tsx
index b9dcff7062..a63c348fbd 100644
--- a/src/components/ageAssurance/AgeAssuranceAppealDialog.tsx
+++ b/src/components/ageAssurance/AgeAssuranceAppealDialog.tsx
@@ -89,7 +89,7 @@ function Inner({control}: {control: Dialog.DialogControlProps}) {
-
+
Contact us
diff --git a/src/components/ageAssurance/AgeAssuranceBadge.tsx b/src/components/ageAssurance/AgeAssuranceBadge.tsx
index 030e30529d..f8b5ee9b29 100644
--- a/src/components/ageAssurance/AgeAssuranceBadge.tsx
+++ b/src/components/ageAssurance/AgeAssuranceBadge.tsx
@@ -29,7 +29,7 @@ export function AgeAssuranceBadge() {
-
+
{success ? Success! : Verify your age}
diff --git a/src/components/ageAssurance/AgeAssuranceRedirectDialog.tsx b/src/components/ageAssurance/AgeAssuranceRedirectDialog.tsx
index b1c287e1b3..c45bc3a2c8 100644
--- a/src/components/ageAssurance/AgeAssuranceRedirectDialog.tsx
+++ b/src/components/ageAssurance/AgeAssuranceRedirectDialog.tsx
@@ -160,7 +160,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
a.pb_md,
]}>
-
+
Success
@@ -209,7 +209,7 @@ export function Inner({}: {optimisticState?: AgeAssuranceRedirectDialogState}) {
]}>
{error && }
-
+
{error ? Connection issue : Verifying}
diff --git a/src/components/ageAssurance/AgeRestrictedScreen.tsx b/src/components/ageAssurance/AgeRestrictedScreen.tsx
index 1430aaaff9..b6a8c26a36 100644
--- a/src/components/ageAssurance/AgeRestrictedScreen.tsx
+++ b/src/components/ageAssurance/AgeRestrictedScreen.tsx
@@ -62,7 +62,7 @@ export function AgeRestrictedScreen({
-
+
You must complete age assurance in order to access this screen.
diff --git a/src/components/dialogs/BirthDateSettings.tsx b/src/components/dialogs/BirthDateSettings.tsx
index 0b8dfb5405..e1c73b67cb 100644
--- a/src/components/dialogs/BirthDateSettings.tsx
+++ b/src/components/dialogs/BirthDateSettings.tsx
@@ -38,7 +38,7 @@ export function BirthDateSettingsDialog({
label={_(msg`My Birthday`)}
style={web({maxWidth: 400})}>
-
+
My Birthday
diff --git a/src/components/dialogs/DeviceLocationRequestDialog.tsx b/src/components/dialogs/DeviceLocationRequestDialog.tsx
index ac33624061..b6547d4f06 100644
--- a/src/components/dialogs/DeviceLocationRequestDialog.tsx
+++ b/src/components/dialogs/DeviceLocationRequestDialog.tsx
@@ -108,7 +108,7 @@ function DeviceLocationRequestDialogInner({onLocationAcquired}: Props) {
return (
-
+
Confirm your location
diff --git a/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx b/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx
index 2b0e86c611..d10d5cab7a 100644
--- a/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx
+++ b/src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx
@@ -153,7 +153,7 @@ export function Disable() {
return (
-
+
Disable email 2FA
@@ -163,7 +163,7 @@ export function Disable() {
style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
To disable your email 2FA method, please verify your access to{' '}
- {currentAccount?.email}
+ {currentAccount?.email}
@@ -214,7 +214,7 @@ export function Disable() {
style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]}>
To disable your email 2FA method, please verify your access to{' '}
- {currentAccount?.email}
+ {currentAccount?.email}
diff --git a/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx b/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx
index bf893701c6..4a3f438df4 100644
--- a/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx
+++ b/src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx
@@ -88,7 +88,7 @@ export function Enable() {
return (
-
+
Enable email 2FA
diff --git a/src/components/dialogs/EmailDialog/screens/Update.tsx b/src/components/dialogs/EmailDialog/screens/Update.tsx
index be0af88076..a0ec69c859 100644
--- a/src/components/dialogs/EmailDialog/screens/Update.tsx
+++ b/src/components/dialogs/EmailDialog/screens/Update.tsx
@@ -199,7 +199,7 @@ export function Update(_props: ScreenProps) {
return (
-
+
Update your email
@@ -239,7 +239,7 @@ export function Update(_props: ScreenProps) {
<>
-
+
Security step required
) {
-
+
Success!
diff --git a/src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx b/src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx
index d6c946956f..77ee6ae094 100644
--- a/src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx
+++ b/src/components/dialogs/EmailDialog/screens/VerificationReminder.tsx
@@ -58,7 +58,7 @@ export function VerificationReminder({
-
+
Please verify your email
diff --git a/src/components/dialogs/EmailDialog/screens/Verify.tsx b/src/components/dialogs/EmailDialog/screens/Verify.tsx
index 07aef6145f..74ff9c96be 100644
--- a/src/components/dialogs/EmailDialog/screens/Verify.tsx
+++ b/src/components/dialogs/EmailDialog/screens/Verify.tsx
@@ -174,7 +174,7 @@ export function Verify({config, showScreen}: ScreenProps) {
return (
-
+
@@ -197,7 +197,7 @@ export function Verify({config, showScreen}: ScreenProps) {
return (
-
+
{state.step === 'email' ? (
state.mutationStatus === 'success' ? (
<>
@@ -239,7 +239,7 @@ export function Verify({config, showScreen}: ScreenProps) {
state.mutationStatus === 'success' ? (
We sent an email to{' '}
-
+
{currentAccount!.email}
{' '}
containing a link. Please click on it to complete the email
@@ -248,7 +248,7 @@ export function Verify({config, showScreen}: ScreenProps) {
) : (
We'll send an email to{' '}
-
+
{currentAccount!.email}
{' '}
containing a link. Please click on it to complete the email
@@ -258,7 +258,7 @@ export function Verify({config, showScreen}: ScreenProps) {
) : (
Please enter the code we sent to{' '}
-
+
{currentAccount!.email}
{' '}
below.
diff --git a/src/components/dialogs/Embed.tsx b/src/components/dialogs/Embed.tsx
index 594800c823..a61004fd2e 100644
--- a/src/components/dialogs/Embed.tsx
+++ b/src/components/dialogs/Embed.tsx
@@ -104,7 +104,7 @@ function EmbedDialogInner({
-
+
Embed post
-
+
Color theme
-
+
External Media
diff --git a/src/components/dialogs/InAppBrowserConsent.tsx b/src/components/dialogs/InAppBrowserConsent.tsx
index 4459c64db9..5ac3ad9165 100644
--- a/src/components/dialogs/InAppBrowserConsent.tsx
+++ b/src/components/dialogs/InAppBrowserConsent.tsx
@@ -62,7 +62,7 @@ function InAppBrowserConsentInner({href}: {href?: string}) {
-
+
How should we open this link?
diff --git a/src/components/dialogs/LinkWarning.tsx b/src/components/dialogs/LinkWarning.tsx
index 9ae8718127..74be78f9d3 100644
--- a/src/components/dialogs/LinkWarning.tsx
+++ b/src/components/dialogs/LinkWarning.tsx
@@ -68,7 +68,7 @@ function InAppBrowserConsentInner({
}>
-
+
{potentiallyMisleading ? (
Potentially misleading link
) : (
@@ -151,7 +151,8 @@ function LinkBox({href}: {href: string}) {
]}>
{scheme}
-
+
{hostname}
{rest}
diff --git a/src/components/dialogs/MutedWords.tsx b/src/components/dialogs/MutedWords.tsx
index e289e1641b..dc9158edeb 100644
--- a/src/components/dialogs/MutedWords.tsx
+++ b/src/components/dialogs/MutedWords.tsx
@@ -108,7 +108,12 @@ function MutedWordsInner() {
+ style={[
+ a.text_md,
+ a.font_semi_bold,
+ a.pb_sm,
+ t.atoms.text_contrast_high,
+ ]}>
Add muted words and tags
@@ -147,7 +152,7 @@ function MutedWordsInner() {
style={[
a.pb_xs,
a.text_sm,
- a.font_bold,
+ a.font_semi_bold,
t.atoms.text_contrast_medium,
]}>
Duration:
@@ -247,7 +252,7 @@ function MutedWordsInner() {
style={[
a.pb_xs,
a.text_sm,
- a.font_bold,
+ a.font_semi_bold,
t.atoms.text_contrast_medium,
]}>
Mute in:
@@ -293,7 +298,7 @@ function MutedWordsInner() {
style={[
a.pb_xs,
a.text_sm,
- a.font_bold,
+ a.font_semi_bold,
t.atoms.text_contrast_medium,
]}>
Options:
@@ -362,7 +367,7 @@ function MutedWordsInner() {
@@ -455,7 +460,7 @@ function MutedWordRow({
style={[
a.flex_1,
a.leading_snug,
- a.font_bold,
+ a.font_semi_bold,
web({
overflowWrap: 'break-word',
wordBreak: 'break-word',
@@ -466,7 +471,8 @@ function MutedWordRow({
{word.value}{' '}
in{' '}
-
+
text & tags
@@ -476,7 +482,8 @@ function MutedWordRow({
{word.value}{' '}
in{' '}
-
+
tags
diff --git a/src/components/dialogs/PostInteractionSettingsDialog.tsx b/src/components/dialogs/PostInteractionSettingsDialog.tsx
index dbb966fd3f..01194ef651 100644
--- a/src/components/dialogs/PostInteractionSettingsDialog.tsx
+++ b/src/components/dialogs/PostInteractionSettingsDialog.tsx
@@ -84,7 +84,7 @@ export function PostInteractionSettingsControlledDialog({
]}>
You can set default interaction settings in{' '}
-
+
Settings → Moderation → Interaction settings
.
@@ -100,7 +100,7 @@ export function PostInteractionSettingsControlledDialog({
export function Header() {
return (
-
+
Post interaction settings
@@ -329,7 +329,7 @@ export function PostInteractionSettingsForm({
-
+
Quote settings
@@ -385,7 +385,7 @@ export function PostInteractionSettingsForm({
opacity: replySettingsDisabled ? 0.3 : 1,
},
]}>
-
+
Reply settings
@@ -535,7 +535,9 @@ function Selectable({
},
style,
]}>
- {label}
+
+ {label}
+
{isSelected ? (
) : (
diff --git a/src/components/dialogs/SearchablePeopleList.tsx b/src/components/dialogs/SearchablePeopleList.tsx
index 4259f3760d..41b31b255c 100644
--- a/src/components/dialogs/SearchablePeopleList.tsx
+++ b/src/components/dialogs/SearchablePeopleList.tsx
@@ -284,7 +284,7 @@ export function SearchablePeopleList({
style={[
a.z_10,
a.text_lg,
- a.font_heavy,
+ a.font_bold,
a.leading_tight,
t.atoms.text_contrast_high,
]}>
diff --git a/src/components/dialogs/StarterPackDialog.tsx b/src/components/dialogs/StarterPackDialog.tsx
index 6a502072ca..13b04e2017 100644
--- a/src/components/dialogs/StarterPackDialog.tsx
+++ b/src/components/dialogs/StarterPackDialog.tsx
@@ -172,7 +172,7 @@ function StarterPackList({
isWeb ? a.mb_2xl : a.my_lg,
a.align_center,
]}>
-
+
Add to starter packs