Skip to content

Revamp Council Page and its components#101

Closed
notkaramel wants to merge 11 commits intomainfrom
antoine/update-council-page
Closed

Revamp Council Page and its components#101
notkaramel wants to merge 11 commits intomainfrom
antoine/update-council-page

Conversation

@notkaramel
Copy link
Member

@vercel
Copy link

vercel bot commented Feb 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ecsess Ready Ready Preview, Comment Feb 10, 2026 5:27am

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request revamps the Council page and its associated components to improve the visual design, accessibility, and user experience. The changes resolve issue #84 regarding the Council Card popup.

Changes:

  • Redesigned council page layout with improved visual hierarchy, section headers, and a responsive grid system
  • Enhanced modal dialog for viewing council member profiles with better accessibility features
  • Added new utility function for generating initials from names with fallback support for missing images
  • Updated CouncilCard and CouncilCardPopUp components with improved styling, image error handling, and responsive design

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
src/routes/council/+page.svelte Complete page redesign with new layout, modal implementation, keyboard navigation, and improved semantic structure
src/routes/council/+page.server.ts Updated image resolution from 300px to 360px and added TypeScript type annotation
src/lib/format.ts New utility function for extracting initials from names with null/undefined safety
src/components/layout/Section.svelte Added contentStart prop for flexible vertical alignment of content
src/components/council/CouncilCardPopUp.svelte Redesigned popup with better accessibility, image fallback, and responsive layout
src/components/council/CouncilCard.svelte Complete redesign with featured/tag support, image fallback, and improved card styling
src/components/council/CouncilAvatar.svelte Updated styling for consistency with new design system

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

);
return [...list].sort((a, b) => {
const aYear = years.findIndex((year) => a.position.includes(year));
const bYear = years.findIndex((year) => b.position.includes(year));
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sorting logic for U-reps uses findIndex which returns -1 when a year is not found in the position string. If a representative's position doesn't include any of the expected year markers (U4, U3, U2, U1, U0), they will be sorted to the beginning (since -1 < any valid index). Consider explicitly handling cases where no year is found, either by filtering them out, placing them at the end, or documenting this behavior.

Suggested change
const bYear = years.findIndex((year) => b.position.includes(year));
const bYear = years.findIndex((year) => b.position.includes(year));
// Place representatives without a recognized U-year marker after those with one
if (aYear === -1 && bYear === -1) return 0;
if (aYear === -1) return 1;
if (bYear === -1) return -1;

Copilot uses AI. Check for mistakes.
{#if image && !imageError}
<img
src={image}
alt={name}
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alt text for council member images simply uses the member's name. For better accessibility, consider making the alt text more descriptive by including context, such as "Photo of [name], [position]" to provide more information to screen reader users.

Suggested change
alt={name}
alt={`Photo of ${name}${position ? `, ${position}` : ''}`}

Copilot uses AI. Check for mistakes.
Comment on lines +70 to +72
return () => {
document.body.style.overflow = '';
};
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cleanup function in the $effect block always resets document.body.style.overflow to an empty string, even when a modal might still be open. If multiple effects or components manage body overflow, this could cause conflicts. The cleanup function will run when the effect re-runs (e.g., when selectedMember changes from one member to another), briefly resetting overflow before the effect body sets it to 'hidden' again. Consider whether this cleanup is necessary given the effect body already handles both cases explicitly.

Suggested change
return () => {
document.body.style.overflow = '';
};

Copilot uses AI. Check for mistakes.
position,
positionDescription,
"image": image.asset->url+"?h=300&fm=webp",
"image": image.asset->url+"?h=360&fm=webp",
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The image height parameter was changed from 300px to 360px. However, the query still specifies only height ('?h=360&fm=webp') without a width parameter. For responsive images, consider whether a width parameter or srcset attributes would be beneficial for optimizing images across different screen sizes, especially since the cards now display at different sizes depending on the 'featured' prop.

Suggested change
"image": image.asset->url+"?h=360&fm=webp",
"image": image.asset->url+"?h=360&w=360&fm=webp",

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +11
let {
name,
position,
email,
positionDescription,
yearProgram,
image,
onClose,
id = 'popup-title'
} = $props();
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The props destructuring lacks TypeScript type annotations, which reduces type safety. Consider adding explicit types, especially since the 'onClose' callback prop is expected but not typed. The type should be something like onClose: () => void to ensure proper contract enforcement.

Copilot uses AI. Check for mistakes.
role="dialog"
aria-modal="true"
aria-labelledby="popup-title"
onclick={(e) => e.target === e.currentTarget && closeModal()}
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The click-outside-to-close functionality uses e.target === e.currentTarget to detect backdrop clicks. While this works, be aware that if the popup content has any margins or if users click between elements in certain edge cases, it might not behave as expected. Consider adding event.stopPropagation() to the inner content div's click handler to ensure clicks inside the modal don't bubble up and trigger closure.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +15
class="group bg-ecsess-800 ring-ecsess-500/50 ring-offset-ecsess-900 hover:ring-ecsess-300 hover:shadow-ecsess-400/50 focus-within:ring-ecsess-300 focus-within:ring-offset-ecsess-900 grid h-full min-w-0 grid-rows-[auto_1fr] overflow-hidden rounded-xl shadow-md ring-2 ring-offset-2 transition-all duration-500 ease-out focus-within:ring-2 focus-within:ring-offset-2 hover:shadow-xl {featured
? 'w-full max-w-56 sm:max-w-64'
: 'w-full max-w-56 sm:max-w-64'}"
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditional expression has identical branches for both featured true and false cases. Both branches return 'w-full max-w-56 sm:max-w-64'. This makes the conditional expression redundant. Either remove the conditional and use a single value, or implement different styling for the featured variant if that was the intention.

Suggested change
class="group bg-ecsess-800 ring-ecsess-500/50 ring-offset-ecsess-900 hover:ring-ecsess-300 hover:shadow-ecsess-400/50 focus-within:ring-ecsess-300 focus-within:ring-offset-ecsess-900 grid h-full min-w-0 grid-rows-[auto_1fr] overflow-hidden rounded-xl shadow-md ring-2 ring-offset-2 transition-all duration-500 ease-out focus-within:ring-2 focus-within:ring-offset-2 hover:shadow-xl {featured
? 'w-full max-w-56 sm:max-w-64'
: 'w-full max-w-56 sm:max-w-64'}"
class="group bg-ecsess-800 ring-ecsess-500/50 ring-offset-ecsess-900 hover:ring-ecsess-300 hover:shadow-ecsess-400/50 focus-within:ring-ecsess-300 focus-within:ring-offset-ecsess-900 grid h-full min-w-0 grid-rows-[auto_1fr] overflow-hidden rounded-xl shadow-md ring-2 ring-offset-2 transition-all duration-500 ease-out focus-within:ring-2 focus-within:ring-offset-2 hover:shadow-xl w-full max-w-56 sm:max-w-64"

Copilot uses AI. Check for mistakes.
<div class="mt-8 flex flex-wrap justify-center gap-4">
{#each vps as vp}
<div
class="flex min-w-full justify-center sm:min-w-[calc(50%-0.5rem)] lg:min-w-[calc(33.333%-0.67rem)] xl:min-w-[calc(25%-0.75rem)]"
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The responsive min-width classes use complex calc() expressions that may not behave as expected in a flex-wrap context. The values sm:min-w-[calc(50%-0.5rem)], lg:min-w-[calc(33.333%-0.67rem)], and xl:min-w-[calc(25%-0.75rem)] attempt to account for gaps, but in a flex container with gap-4 (1rem), these calculations don't correctly align with the actual gap size. Consider using a simpler approach with flex-basis or adjusting the calculations to match the actual gap value of 1rem.

Suggested change
class="flex min-w-full justify-center sm:min-w-[calc(50%-0.5rem)] lg:min-w-[calc(33.333%-0.67rem)] xl:min-w-[calc(25%-0.75rem)]"
class="flex justify-center basis-full shrink-0 sm:basis-1/2 lg:basis-1/3 xl:basis-1/4"

Copilot uses AI. Check for mistakes.
@vercel
Copy link

vercel bot commented Feb 9, 2026

Deployment failed with the following error:

Creating the Deployment Timed Out.

@notkaramel notkaramel closed this Feb 10, 2026
@notkaramel notkaramel deleted the antoine/update-council-page branch February 24, 2026 20:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Revamp to Council Card popup

2 participants