Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 21 additions & 9 deletions src/components/common/CreatorSkeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ import { cn } from '@/lib/utils';

interface CreatorSkeletonProps {
className?: string;
disableShimmer?: boolean;
}

const skeletonBlockClass =
'rounded-md bg-white/12 skeleton-shimmer motion-reduce:bg-white/18 motion-reduce:ring-1 motion-reduce:ring-white/15';
const skeletonStaticBlockClass =
'rounded-md bg-white/16 ring-1 ring-white/15';

const CreatorSkeleton: React.FC<CreatorSkeletonProps> = ({
className,
disableShimmer = false,
}) => {
const blockClass = disableShimmer ? skeletonStaticBlockClass : skeletonBlockClass;

const CreatorSkeleton: React.FC<CreatorSkeletonProps> = ({ className }) => {
return (
<div
className={cn(
Expand All @@ -18,33 +26,37 @@ const CreatorSkeleton: React.FC<CreatorSkeletonProps> = ({ className }) => {
<div
className={cn(
'mb-4 aspect-square w-full rounded-xl',
skeletonBlockClass
blockClass
)}
/>

<div className="mb-4 space-y-2">
<div className={cn('h-6 w-3/4', skeletonBlockClass)} />
<div className={cn('h-4 w-1/2', skeletonBlockClass)} />
<div className={cn('h-6 w-3/4', blockClass)} />
<div className={cn('h-4 w-1/2', blockClass)} />
</div>

<div className="flex items-center justify-between">
<div className="space-y-1">
<div className={cn('h-3 w-12', skeletonBlockClass)} />
<div className={cn('h-6 w-16', skeletonBlockClass)} />
<div className={cn('h-3 w-12', blockClass)} />
<div className={cn('h-6 w-16', blockClass)} />
</div>
<div className={cn('h-9 w-24 rounded-xl', skeletonBlockClass)} />
<div className={cn('h-9 w-24 rounded-xl', blockClass)} />
</div>
</div>
);
};

export const CreatorGridSkeleton: React.FC<{ count?: number }> = ({
export const CreatorGridSkeleton: React.FC<{
count?: number;
disableShimmer?: boolean;
}> = ({
count = 6,
disableShimmer = false,
}) => {
return (
<div className="grid w-full grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{Array.from({ length: count }).map((_, i) => (
<CreatorSkeleton key={i} />
<CreatorSkeleton key={i} disableShimmer={disableShimmer} />
))}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/CreatorSocialLinksList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const CreatorSocialLinksList: React.FC<CreatorSocialLinksListProps> = ({
href={url}
target="_blank"
rel="noreferrer"
className="group inline-flex w-full items-center justify-center gap-1.5 rounded-lg border border-white/10 bg-white/[0.03] px-2.5 py-2 text-[11px] font-semibold text-white/80 transition-colors hover:border-amber-400/40 hover:bg-amber-400/10 hover:text-amber-100"
className="link-action-chip group inline-flex w-full items-center justify-center gap-1.5 rounded-lg border border-white/10 bg-white/[0.03] px-2.5 py-2 text-[11px] font-semibold text-white/80 transition-colors hover:border-amber-400/40 hover:bg-amber-400/10 hover:text-amber-100"
>
<Icon className="size-3.5" />
<span>{label}</span>
Expand Down
18 changes: 18 additions & 0 deletions src/components/common/__tests__/CreatorSkeleton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,28 @@ describe('CreatorSkeleton', () => {
expect(shimmerBlocks).toHaveLength(6);
});

it('supports a static non-animated fallback when shimmer is disabled', () => {
const { container } = render(<CreatorSkeleton disableShimmer />);
const shimmerBlocks = container.querySelectorAll('.skeleton-shimmer');

expect(shimmerBlocks).toHaveLength(0);
expect(container.querySelectorAll('.ring-white\\/15')).toHaveLength(6);
});

it('renders the requested number of cards in grid skeleton', () => {
const { container } = render(<CreatorGridSkeleton count={3} />);
const shimmerBlocks = container.querySelectorAll('.skeleton-shimmer');

expect(shimmerBlocks).toHaveLength(18);
});

it('propagates disableShimmer to all cards in the grid skeleton', () => {
const { container } = render(
<CreatorGridSkeleton count={2} disableShimmer />
);
const shimmerBlocks = container.querySelectorAll('.skeleton-shimmer');

expect(shimmerBlocks).toHaveLength(0);
expect(container.querySelectorAll('.ring-white\\/15')).toHaveLength(12);
});
});
13 changes: 13 additions & 0 deletions src/components/common/__tests__/CreatorSocialLinksList.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import CreatorSocialLinksList from '../CreatorSocialLinksList';

describe('CreatorSocialLinksList', () => {
it('renders link-only action chips with keyboard-visible focus styling hook', () => {
render(<CreatorSocialLinksList handle="creator" />);

const xLink = screen.getByRole('link', { name: /x/i });
expect(xLink.className).toContain('link-action-chip');
expect(xLink.className).toContain('hover:border-amber-400/40');
});
});
21 changes: 21 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,27 @@
margin-bottom: var(--spacing-section-major);
}

.link-action-chip:focus-visible {
outline: none;
border-color: rgba(252, 211, 77, 0.85);
background-color: rgba(251, 191, 36, 0.2);
color: rgba(255, 255, 255, 0.98);
box-shadow:
0 0 0 2px rgba(252, 211, 77, 0.9),
0 0 0 5px rgba(15, 23, 42, 0.95);
}

@media (forced-colors: active) {
.link-action-chip:focus-visible {
outline: 2px solid Highlight;
outline-offset: 2px;
border-color: Highlight;
background-color: Canvas;
color: CanvasText;
box-shadow: none;
}
}

.skeleton-shimmer {
position: relative;
overflow: hidden;
Expand Down
Loading