Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dc78eb9
feat(cards): glass floating action bar on feed cards behind a flag
tsahimatsliah Jun 9, 2026
d298b6e
feat(cards): extend glass floating actions to share/collection/freeform
tsahimatsliah Jun 9, 2026
ea15f12
feat(cards): collapse glass action bar to a compact peek until hover
tsahimatsliah Jun 10, 2026
2b647ea
feat(cards): make glass card cover image full-bleed
tsahimatsliah Jun 10, 2026
ed94746
feat(cards): morph glass bar on hover + apply to all post types
tsahimatsliah Jun 10, 2026
d10a334
fix(cards): glass bar expands to full width in original order
tsahimatsliah Jun 10, 2026
a4256f2
fix(cards): refine glass bar — padding, transition, colors, shadow
tsahimatsliah Jun 10, 2026
9ac04a5
feat(cards): morph glass bar as one breathing pill
tsahimatsliah Jun 10, 2026
7cff6bd
feat(cards): liquid-glass morph — anchored buttons, metric-only peek
tsahimatsliah Jun 10, 2026
3b6722f
fix(cards): symmetric edge padding on collapsed glass pill
tsahimatsliah Jun 10, 2026
1121a18
fix(cards): theme-aware glass pill via blur-bg token
tsahimatsliah Jun 10, 2026
b89c65b
fix(tailwind): make var-based theme tokens alpha-capable
tsahimatsliah Jun 10, 2026
b7c8431
feat(cards): reorder glass actions, max-contrast icons, shorter cards
tsahimatsliah Jun 10, 2026
eda9a28
fix(cards): stop glass bar expanding while the feed scrolls
tsahimatsliah Jun 10, 2026
bf9d26a
feat(cards): full-bleed glass cover image (same height/crop)
tsahimatsliah Jun 10, 2026
9fe5003
fix(cards): restore brand hover color on glass action icon + counter
tsahimatsliah Jun 10, 2026
10aae61
fix(lint): reorder classes flagged after alpha-token change
tsahimatsliah Jun 10, 2026
afdb88b
fix(cards): remove hover-intent delay on glass bar expand
tsahimatsliah Jun 10, 2026
37be532
feat(cards): glass floating bar on the hero/featured wide card
tsahimatsliah Jun 14, 2026
db2a063
feat(cards): scrim behind glass bar + full-bleed video thumbnail
tsahimatsliah Jun 14, 2026
e69ca8a
fix(cards): hero glass bar on the content side, not over the image
tsahimatsliah Jun 14, 2026
df66200
fix(lint): reorder classes in giveback components after alpha-token c…
tsahimatsliah Jun 15, 2026
8e5cc0a
fix(cards): match video tint to the full-bleed glass cover
tsahimatsliah Jun 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 43 additions & 11 deletions packages/shared/src/components/cards/Freeform/FreeformGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ import { SquadPostCardHeader } from '../common/SquadPostCardHeader';
import PostMetadata from '../common/PostMetadata';
import { WelcomePostCardFooter } from '../common/WelcomePostCardFooter';
import ActionButtons from '../common/ActionButtons';
import {
FeedCardGlassActions,
glassCoverImageClassName,
} from '../common/FeedCardGlassActions';
import { ClickbaitShield } from '../common/ClickbaitShield';
import { useSmartTitle } from '../../../hooks/post/useSmartTitle';
import { useFeedCardGlassActions } from '../../../hooks/useFeedCardGlassActions';
import { useHiddenFeedbackPanel } from '../../../hooks/post/useHiddenFeedbackPanel';

export const FreeformGrid = forwardRef(function SharePostCard(
Expand All @@ -43,6 +48,11 @@ export const FreeformGrid = forwardRef(function SharePostCard(
const image = usePostImage(post);
const { title } = useSmartTitle(post);
const { isHidden, content: hiddenPanel } = useHiddenFeedbackPanel(post);
const glassActions = useFeedCardGlassActions();
// The floating glass bar applies to every freeform post. When there's a cover
// image it floats over it full-bleed; for text/markdown posts it floats over
// the bottom of the content (which blurs through the glass).
const useGlass = glassActions;

if (isHidden) {
return (
Expand All @@ -64,7 +74,11 @@ export const FreeformGrid = forwardRef(function SharePostCard(
<FeedItemContainer
domProps={{
...domProps,
className: getPostClassNames(post, domProps.className, 'min-h-card'),
className: getPostClassNames(
post,
domProps.className,
useGlass ? 'min-h-cardGlass' : 'min-h-card',
),
}}
ref={ref}
flagProps={{ pinnedAt, trending }}
Expand Down Expand Up @@ -110,21 +124,39 @@ export const FreeformGrid = forwardRef(function SharePostCard(
readTime={post.readTime}
/>
</>
<Container ref={containerRef}>
<Container
ref={containerRef}
className={useGlass && image ? 'flex-none' : undefined}
>
<WelcomePostCardFooter
image={image}
contentHtml={post.contentHtml}
post={post}
imageClassName={
useGlass && image ? glassCoverImageClassName : undefined
}
/>
<ActionButtons
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
className="mt-auto"
onDownvoteClick={onDownvoteClick}
/>
{useGlass ? (
<FeedCardGlassActions
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
onDownvoteClick={onDownvoteClick}
coverScrim={!!image}
/>
) : (
<ActionButtons
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
className="mt-auto"
onDownvoteClick={onDownvoteClick}
/>
)}
</Container>
{children}
</FeedItemContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import { PostCardHeader } from '../common/PostCardHeader';
import PostTags from '../common/PostTags';
import PostMetadata from '../common/PostMetadata';
import ActionButtons from '../common/ActionButtons';
import { FeedCardGlassActions } from '../common/FeedCardGlassActions';
import { FeedbackGrid } from './feedback/FeedbackGrid';
import { ClickbaitShield } from '../common/ClickbaitShield';
import { useSmartTitle } from '../../../hooks/post/useSmartTitle';
import { useFeedCardGlassActions } from '../../../hooks/useFeedCardGlassActions';
import { usePostImage } from '../../../hooks/post/usePostImage';
import { useCardCover } from '../../../hooks/feed/useCardCover';
import { HIGH_PRIORITY_IMAGE_PROPS, Image, ImageType } from '../../image/Image';
Expand Down Expand Up @@ -149,6 +151,12 @@ export const ArticleFeaturedWideGridCard = forwardRef(
const isVideoType = isVideoPost(post);
const image = usePostImage(post);
const { overlay } = useCardCover({ post, onShare });
const glassActions = useFeedCardGlassActions();
// The hero floats the glass pill on the content (left) column where the
// action bar has always sat — not over the cover image — and shrinks to the
// glass height so it lines up with the other glass cards in a row. The
// feedback state keeps its own layout.
const useGlass = glassActions && !showFeedback;
const significance = post.hero?.significance;
const isTweetPost =
post.type === PostType.SocialTwitter ||
Expand Down Expand Up @@ -217,7 +225,7 @@ export const ArticleFeaturedWideGridCard = forwardRef(
className: getPostClassNames(
post,
classNames(className ?? '', 'h-full overflow-hidden'),
'min-h-card',
useGlass ? 'min-h-cardGlass' : 'min-h-card',
),
}}
ref={ref}
Expand All @@ -236,7 +244,7 @@ export const ArticleFeaturedWideGridCard = forwardRef(
INNER_GRID_COLS[wideColSpan],
)}
>
<div className="flex min-h-0 min-w-0 flex-col overflow-hidden">
<div className="relative flex min-h-0 min-w-0 flex-col overflow-hidden">
{showFeedback ? (
<>
<h3 className="line-clamp-2 break-words px-6 pt-6 font-bold text-text-primary typo-title3">
Expand Down Expand Up @@ -290,18 +298,29 @@ export const ArticleFeaturedWideGridCard = forwardRef(
</p>
) : null}
</CardTextContainer>
<Container>
<CardSpace />
<ActionButtons
{useGlass ? (
<FeedCardGlassActions
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
onDownvoteClick={onDownvoteClick}
variant="grid"
/>
</Container>
) : (
<Container>
<CardSpace />
<ActionButtons
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
onDownvoteClick={onDownvoteClick}
variant="grid"
/>
</Container>
)}
</>
)}
</div>
Expand Down
45 changes: 33 additions & 12 deletions packages/shared/src/components/cards/article/ArticleGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ import PostTags from '../common/PostTags';
import PostMetadata from '../common/PostMetadata';
import { PostCardFooter } from '../common/PostCardFooter';
import ActionButtons from '../common/ActionButtons';
import {
FeedCardGlassActions,
glassCoverImageClassName,
} from '../common/FeedCardGlassActions';
import { FeedbackGrid } from './feedback/FeedbackGrid';
import { ClickbaitShield } from '../common/ClickbaitShield';
import { useSmartTitle } from '../../../hooks/post/useSmartTitle';
import { useFeedCardGlassActions } from '../../../hooks/useFeedCardGlassActions';

export const ArticleGrid = forwardRef(function ArticleGrid(
{
Expand Down Expand Up @@ -55,6 +60,7 @@ export const ArticleGrid = forwardRef(function ArticleGrid(
const { showFeedback } = usePostFeedback({ post });
const { title } = useSmartTitle(post);
const isVideoType = isVideoPost(post);
const glassActions = useFeedCardGlassActions();

if (isHidden) {
return (
Expand Down Expand Up @@ -91,7 +97,7 @@ export const ArticleGrid = forwardRef(function ArticleGrid(
className: getPostClassNames(
post,
classNames(className, showFeedback && '!p-0'),
'min-h-card',
glassActions && !showFeedback ? 'min-h-cardGlass' : 'min-h-card',
),
}}
ref={ref}
Expand Down Expand Up @@ -150,27 +156,42 @@ export const ArticleGrid = forwardRef(function ArticleGrid(
/>
</Container>
)}
<Container>
<Container className={glassActions ? 'flex-none' : undefined}>
<PostCardFooter
openNewTab={openNewTab ?? false}
post={post}
onShare={onShare}
className={{
image: classNames('px-1', showFeedback && 'mb-0'),
cover:
glassActions && !showFeedback
? glassCoverImageClassName
: undefined,
}}
eagerLoadImage={eagerLoadImage}
/>

{!showFeedback && (
<ActionButtons
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
onDownvoteClick={onDownvoteClick}
/>
)}
{!showFeedback &&
(glassActions ? (
<FeedCardGlassActions
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
onDownvoteClick={onDownvoteClick}
coverScrim
/>
) : (
<ActionButtons
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
onDownvoteClick={onDownvoteClick}
/>
))}
</Container>
</div>
{children}
Expand Down
47 changes: 36 additions & 11 deletions packages/shared/src/components/cards/collection/CollectionGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ import {
} from '../common/Card';
import { WelcomePostCardFooter } from '../common/WelcomePostCardFooter';
import ActionButtons from '../common/ActionButtons';
import {
FeedCardGlassActions,
glassCoverImageClassName,
} from '../common/FeedCardGlassActions';
import PostMetadata from '../common/PostMetadata';
import { usePostImage } from '../../../hooks/post/usePostImage';
import { useFeedCardGlassActions } from '../../../hooks/useFeedCardGlassActions';
import CardOverlay from '../common/CardOverlay';
import PostTags from '../common/PostTags';
import { isPostUpdated } from '../../../graphql/posts';
Expand Down Expand Up @@ -42,6 +47,11 @@ export const CollectionGrid = forwardRef(function CollectionCard(
const onPostCardClick = () => onPostClick?.(post);
const onPostCardAuxClick = () => onPostAuxClick?.(post);
const { isHidden, content: hiddenPanel } = useHiddenFeedbackPanel(post);
const glassActions = useFeedCardGlassActions();
// The floating glass bar applies to every collection. When there's a cover
// image it floats over it full-bleed; otherwise it floats over the bottom of
// the content (which blurs through the glass).
const useGlass = glassActions;

if (isHidden) {
return (
Expand Down Expand Up @@ -70,7 +80,7 @@ export const CollectionGrid = forwardRef(function CollectionCard(
className: getPostClassNames(
post,
domProps.className ?? '',
'min-h-card',
useGlass ? 'min-h-cardGlass' : 'min-h-card',
),
}}
ref={ref}
Expand Down Expand Up @@ -106,22 +116,37 @@ export const CollectionGrid = forwardRef(function CollectionCard(
numSources={post.numCollectionSources}
className={classNames('mx-4', post.image ? 'my-0' : 'mb-4 mt-2')}
/>
<Container>
<Container className={useGlass && image ? 'flex-none' : undefined}>
<WelcomePostCardFooter
image={image}
contentHtml={post.contentHtml}
post={post}
onShare={onShare}
imageClassName={
useGlass && image ? glassCoverImageClassName : undefined
}
/>
<ActionButtons
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
className="mt-auto"
onDownvoteClick={onDownvoteClick}
/>
{useGlass ? (
<FeedCardGlassActions
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
onDownvoteClick={onDownvoteClick}
coverScrim={!!image}
/>
) : (
<ActionButtons
post={post}
onUpvoteClick={onUpvoteClick}
onCommentClick={onCommentClick}
onCopyLinkClick={onCopyLinkClick}
onBookmarkClick={onBookmarkClick}
className="mt-auto"
onDownvoteClick={onDownvoteClick}
/>
)}
</Container>
{children}
</FeedItemContainer>
Expand Down
Loading
Loading