Styling and performance
The components come unstyled apart from a few layout essentials, with a stable class on every element so your site's CSS can make them its own. This page lists every class hook, explains the inline styles, and covers what keeps listing pages fast.
Class hooks
Every component also accepts a className, added next to its root class.
ListingCard and ListingGrid
| Class | Element |
|---|---|
re-listing-grid | The grid container |
re-listing-card__link | The <a> around a card, when it has a link |
re-listing-card | The card |
re-listing-card__media | The photo frame |
re-listing-card__image | The photo |
re-listing-card__price | The price, inside the photo frame |
re-listing-card__status | The status badge, for listings that aren't active |
re-listing-card__body | The text below the photo |
re-listing-card__address | The street address |
re-listing-card__location | City, state and ZIP |
re-listing-card__specs | Beds, baths and square feet |
re-listing-card__courtesy | "Courtesy of" the listing brokerage |
The price and status sit inside the photo frame, which is position: relative, so you can place them over the photo:
.re-listing-card__price {
position: absolute;
left: 0.75rem;
bottom: 0.75rem;
padding: 0.25rem 0.6rem;
border-radius: 999px;
background: rgba(0, 0, 0, 0.7);
color: #fff;
font-weight: 600;
}ListingDetail
| Class | Element |
|---|---|
re-listing-detail | The <article> |
re-listing-detail__gallery | The photo strip |
re-listing-detail__image | Each photo in the strip |
re-listing-detail__header | The header |
re-listing-detail__address | The <h1> |
re-listing-detail__location | City, state and ZIP |
re-listing-detail__price | The price |
re-listing-detail__specs | Beds, baths and square feet |
re-listing-detail__facts | The facts <dl> |
re-listing-detail__fact | Each fact (a <div> holding a <dt> and <dd>) |
re-listing-detail__description | The description |
re-listing-detail__attribution | The "Listed by" line |
ListingGallery
| Class | Element |
|---|---|
re-gallery | The gallery (focusable, for arrow keys) |
re-gallery__main | The main photo frame |
re-gallery__image | The main photo |
re-gallery__prev, re-gallery__next | The previous and next buttons |
re-gallery__count | The "3 / 18" counter |
re-gallery__thumbs | The thumbnail strip |
re-gallery__thumb | Each thumbnail button |
re-gallery__thumb--active | The current thumbnail |
ListingFilters
| Class | Element |
|---|---|
re-listing-filters | The <form> |
re-listing-filters__field | Each <label> wrapping a field |
re-listing-filters__field--keyword | The search box's label, as well |
re-listing-filters__submit | The submit button |
ListingPagination
| Class | Element |
|---|---|
re-listing-pagination | The <nav> |
re-listing-pagination__prev, re-listing-pagination__next | The previous and next links |
re-listing-pagination__disabled | Added to either one when it can't go anywhere |
re-listing-pagination__status | "Page 2 of 9 (104 listings)" |
IdxAttribution and ListingInquiryForm
| Class | Element |
|---|---|
re-idx-attribution | The attribution block |
re-idx-attribution__courtesy | "Listing courtesy of" line |
re-idx-attribution__updated | "MLS data last updated" line |
re-inquiry-form | The <div> around the managed form. The form itself is styled like any site-kit form. |
Overriding inline styles
A handful of layout essentials are inline styles, so the components look reasonable before you've written any CSS:
| Element | Inline styles |
|---|---|
re-listing-grid | display: grid, 320px minimum columns, gap: 1.5rem |
re-listing-card__link | display: block, inherited color, no underline |
re-listing-card__media | 16:10 aspect ratio, overflow: hidden, gray background, position: relative |
re-listing-card__image, re-gallery__image | Fill the frame with object-fit: cover |
re-listing-card__courtesy | Smaller font, 0.7 opacity, small top margin |
re-listing-detail__gallery | Responsive grid of 280px minimum columns |
re-listing-detail__image | 4:3 aspect ratio, object-fit: cover |
re-listing-detail__attribution | Smaller font, 0.7 opacity |
re-gallery__main | 16:10 aspect ratio, gray background |
re-gallery__prev, re-gallery__next, re-gallery__count, thumbnails | Round dark buttons and counter, thumbnail sizing |
re-gallery__thumb | outline-offset: -2px, so the focus ring draws inside the thumbnail where the scrolling strip can't clip it |
re-listing-filters and its fields | Flex row that wraps, small labels stacked over inputs |
re-listing-pagination | Centered flex row |
re-idx-attribution | Smaller font, 0.75 opacity |
The kit never uses !important, so everything else is yours. Inline styles do win over class rules, though, so to change one of the properties above, either mark your rule !important:
.re-listing-grid {
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)) !important;
}or skip the component that sets it. For a different grid, map ListingCard inside your own container (see Cards and grids).
Focus styles
The gallery takes keyboard focus so the arrow keys work. It shows the browser's own focus ring when it's focused from the keyboard, and not after a mouse click. The kit doesn't set an outline on it, so a plain class rule restyles the ring, no !important needed:
.re-gallery:focus-visible {
outline: 3px solid #2563eb;
outline-offset: 4px;
border-radius: 0.5rem;
}Keep a visible style there, and on the gallery's buttons and thumbnails, so keyboard users can always see where they are. Don't set outline: none without a replacement.
Performance
Listing pages should render on the server and paint fast on a phone. The kit is built for that; these are the things to keep in mind.
- Almost everything renders on the server. The root and
/serverentry points ship no client JavaScript. A listing page's only client code is the gallery, the inquiry form and the view tracker (which renders nothing). - Keep the tracker a childless sibling.
ListingViewTrackerreturnsnulland never wraps content. Don't put page content inside it, or inside any provider: wrapping the page in a client component turns a prerendered page into a client-rendered one. - Pass client components only what they read. Props sent to
ListingGalleryandListingInquiryFormtravel to the browser with the page. Give the form its four listing fields, not the whole listing (see Inquiry forms). - Photos are plain
<img>tags from the MLS's image host. Cards and thumbnails load lazily.ListingDetailloads its first photo eagerly and the rest lazily, and the gallery's main photo loads eagerly. - Don't animate the first photo in. The top photo is usually the largest thing on screen when the page loads. Leave it visible from the first paint, with no fade or slide-in, and save entrance animations for content further down.
- Check the HTML. After a build,
curla listing page and confirm the address, price and card markup are in the response. If you only see a script payload, something above the listing content has become a client component.