Custom CSS / JS

Your CSS already has a way in

Every FotoGrids render ships a unique DOM ID, 200+ frontend classes, 300+ data-fg-* attributes and roughly 600+ CSS custom properties, about half of them emitted per Gallery instance. All of it is in Free, and none of it needs a plugin setting.

Anatomy

This is what a Gallery renders

Not a simplified diagram — the markup below is what FotoGrids writes for a masonry Gallery with one item. Every hook you need to style is visible in the following lines.

				
					<!--
One instance id, the umbrella class, and exactly one discriminator class.
data-fg-gallery-id is always written first. Layout is an attribute, never a class.
-->
<div id="fg-42-1" class="fotogrids-collection fotogrids-gallery"
     data-fg-gallery-id="42" data-fg-layout="masonry" data-fg-total-items="24">

  <!--
  Wraps the track only when the layout supports pagination.
  Single Item and Slider opt out, so they render their track without it. 
  -->
  <div class="fg-layout-body">

    <!--
    Seven track containers: .fg-grid-track, .fg-masonry-track, .fg-justified-track,
    .fg-carousel-track (the Slider layout and Pro's Carousel share it),
    .fg-instant-photos-track, .fg-single-item-track, .fg-viewer-track.
    Featured Item uses .fg-featured-grid instead.
    -->
    <div class="fg-masonry-track">

      <!--
      data-fg-media-state flips loading -> loaded,
      so you can style the frame before the image paints.
      -->
      <figure class="fg-item" data-fg-media-state="loaded">
        <div class="fg-item-media">

          <!-- The Lightbox, external-link and direct-link decorators add data-fg-item-id to this anchor -->
          <a href="…" data-fg-item-id="118">

            <!-- Both sources stay on the element after load -->
            <img decoding="async" src="…" data-fg-thumb-src="…" data-fg-full-src="…"
                 data-fg-thumb-w="400" data-fg-thumb-h="300">

            <!--
            One .fg-overlay span per configured item overlay, each carrying a
            position class and its own content. Absent when none are configured
            -->
            <span class="fg-overlay"></span>

            <div class="fg-item-loader" aria-hidden="true"></div>
          </a>
        </div>

        <figcaption class="fg-caption">

          <!-- The caption backdrop is its own element — restyle it without touching the text -->
          <span class="fg-caption-bg" aria-hidden="true"></span>

          <span class="fg-caption-content">

            <!-- Title and description are separate nodes, each with its own line-clamp variable -->
            <span class="fg-caption-title">Low tide, 6am</span>
            <span class="fg-caption-description">Shot on the north beach</span>
          </span>
        </figcaption>
      </figure>
    </div>
  </div>
</div>

				
			
The id format is fg-{collectionId}-{n}, where n is a 1-based per-request counter. Implement the same Gallery on a page twice and you get #fg-42-1 and #fg-42-2 — two addressable instances of one Gallery.
Targeting

Three levels, one cascade

Site-wide, per-Gallery, or one instance on one page. Pick the level, write the rule, done — no per-Gallery setting stands between you and the element.
CSS
fotogrids-targeting.css
CSS
				
					/* SCOPE — how wide the rule reaches */

/* 1 — every Gallery and Album on the site */
.fotogrids-collection { }

/* 2 — every render of Gallery 42, wherever it appears */
[data-fg-gallery-id="42"] { }

/* 3 — one instance on one page */
#fg-42-1 { }

/* CONFIGURATION — narrow by how it is set up */

/* every masonry Gallery, at any scope above */
.fotogrids-collection[data-fg-layout="masonry"] { }

/* the frame before the image paints */
.fg-item[data-fg-media-state="loading"] { }
				
			

The wrapper contract, quoted from the source

Every wrapper carries the umbrella class fotogrids-collection plus exactly one discriminator — fotogrids-gallery or fotogrids-album. Nothing else about the render is expressed as a class. All decorator and feature state is written as data-fg-* attributes, and data-fg-gallery-id is always written first.

That split is the whole design. Classes tell you what kind of thing an element is. Attributes tell you how it is configured. You style the first and read the second.

Level 2 is the one most people want and miss. #fg-42-1 is precise but instance-bound; [data-fg-gallery-id="42"] follows Gallery 42 onto every page it appears on, in every builder.

Scope and configuration compose. Put an attribute on a scope selector and you get the intersection — #fg-42-1[data-fg-layout="masonry"], or every masonry Gallery on the site, or only the ones inside a particular Album.

Cheat Sheet

What you can select

170 distinct .fg-* classes and 250+ data-fg-* attributes ship on the frontend. These are the ones worth memorising.
IDs & Wrappers
#fg-42-1
One rendered instance. Format fg-{collectionId}-{n}, n a 1-based per-request counter.
.fotogrids-collection

The umbrella class on every Gallery and Album wrapper.

.fotogrids-gallery
The discriminator on a Gallery wrapper.
.fotogrids-album
The discriminator on an Album wrapper.
#fotogrids-view
The body id on the standalone View Page shell.
.fotogrids-view--gallery

View Page body class.
Also --album, --theme-dark, --theme-light.

Classes
.fg-item & .fg-item-media

The item figure and its media box.

Plus .fg-overlay, .fg-item-loader, .fg-item-hidden.

.fg-caption-title & .fg-caption-description
Separate caption nodes, under .fg-caption-content and .fg-caption-bg.
.fg-masonry-track
One track container per layout — also .fg-grid-track, .fg-justified-track, .fg-carousel-track, .fg-instant-photos-track, .fg-single-item-track, .fg-viewer-track, plus .fg-featured-grid for Featured Item, all inside .fg-layout-body — except Single Item and Slider, which opt out of pagination and so render their track without that wrapper.
.fg-pagination--load-more
Double-hyphen modifiers mark variants. Also --pages, --endless-scroll, --exhausted.
.fg-is-open
A dedicated state family: fg-is-active, fg-is-closing, fg-is-loading, fg-is-zooming, fg-is-swapping, fg-is-leaving, fg-is-source, fg-is-trimmed.
.fg-lb-grid-tile, .fg-filter-btn & .fg-gate-card
Lightbox, filter UI and password-gate surfaces are namespaced the same way.
Data Attributes
[data-fg-gallery-id] & [data-fg-album-id]
Follows one collection across every page it renders on.
[data-fg-layout]
Eight values in Free — grid, masonry, justified, slider, featured-item, instant-photos, image-viewer, single-item.
[data-fg-media-state]
Reads loading, then loaded. Style the placeholder frame off this.
[data-fg-item-id] & [data-fg-item-type]
[data-fg-item-id] — on the <a> that wraps each item, added by the Lightbox, external-link and direct-link decorators. [data-fg-item-type] — on the .fg-video element inside video items.
[data-fg-natural-ratio="1"]
On the wrapper, not the item. Present only when the item aspect ratio is set to None, which opts the render out of the fixed item-box rules.
[data-fg-page-current] & [data-fg-page-total]
Pagination state, with data-fg-page-size, data-fg-paginated and data-fg-total-items.
[data-fg-hover] & [data-fg-loaded-effect]
Which effect a render is configured with. Also data-fg-lightbox-variant, data-fg-columns-mode, data-fg-restricted, data-fg-scope.
CSS Variables

Every value in the render is a variable

Variable
What it controls
Box & grid

--fg-cols

Column count on the layout track.
--fg-col-min
Minimum column width.
--fg-col-max
Maximum column width.
--fg-gap
Gap between items.
--fg-item-aspect-ratio
Aspect ratio applied to the item box.
--fg-item-fit
Fit behaviour of the media inside the item box.
--fg-radius
Corner radius on the item box.
--fg-border-w, --fg-border-color & --fg-border-style
Item border width, colour and style.
--fg-padding & --fg-margin
Padding and margin on the item box.
Layout-specific
--fg-justified-row-height
Row height for the justified layout.
--fg-featured-grid-cols & --fg-featured-grid-rows
Grid dimensions for the featured-item layout.
--fg-items-per-view
Items visible at once in slider and carousel layouts.
--fg-height-fixed & --fg-height-max
Fixed and maximum height for the layout track.
View Page
--fg-view-accent & --fg-view-max-width
Accent colour and content width on the standalone View Page.
--fg-view-bg, --fg-view-fg, --fg-view-border & --fg-view-muted
The View Page colour set, light and dark.
Every variable in this table is emitted per render, under #fg-{collectionId}-{n}. Override any of them from your own stylesheet at whichever of the three targeting levels fits.
Variable
What it controls
Caption text
--fg-caption-title-color
Colour of .fg-caption-title.
--fg-caption-title-font-size
Title size.
--fg-caption-title-lines
Line clamp on the title.
--fg-caption-desc-color
Colour of .fg-caption-description.
--fg-caption-desc-font-size
Description size.
--fg-caption-desc-lines
Line clamp on the description.
Caption box
--fg-caption-overlay-bg
Backdrop behind the caption, drawn on .fg-caption-bg. Takes a gradient.
--fg-caption-overlay-bg-hover
The same backdrop on hover.
--fg-caption-align
Alignment of the caption content.
--fg-caption-gap
Space between title and description.
--fg-caption-padding
Padding inside the caption box.
Variable
What it controls
Hover
--fg-hover-duration & --fg-hover-ease
Timing and easing of the hover transition.
--fg-hover-zoom-scale
Scale factor for the zoom hover effect.
--fg-hover-lift-distance
Travel for the lift hover effect.
--fg-hover-tilt-angle
Angle for the tilt hover effect.
--fg-hover-tint-color
Tint laid over the item on hover.
--fg-hover-frame-color
Frame colour for the frame hover effect.
--fg-hover-filter
Filter applied to the media on hover.
--fg-hover-cursor & --fg-hover-cursor-link
Cursor over an item, and over an item that links out.
Shadow
--fg-shadow
Shadow on the item box.
--fg-shadow-x, --fg-shadow-y & --fg-shadow-blur
Shadow offset and blur.
--fg-shadow-hover
Shadow on hover.
--fg-elev-shadow-*
The elevation shadow set used by the shadow decorator.
Tooltip
--fg-tt-bg & --fg-tt-color
Tooltip background and text colour.
--fg-tt-radius, --fg-tt-arrow-size & --fg-tt-z
Tooltip radius, arrow size and stacking order.
Variable
What it controls
Lightbox — the largest family
--fg-lb-bg
Lightbox backdrop.
--fg-lb-z
Stacking order of the Lightbox layer.
--fg-lb-toolbar-*
The toolbar set — surfaces styled through .fg-lb-grid-toolbar.
--fg-lb-thumb-*
The thumbnail strip set.
--fg-lb-nav-*
The previous/next control set, on .fg-lb-grid-nav.
--fg-lb-info-*
The info panel set.
--fg-lb-color-*
The shared Lightbox colour set.
--fg-lb-grid-*
The grid Lightbox variant — .fg-lb-grid-tile, .fg-lb-grid-stage, .fg-lb-grid-counter.
--fg-lb-mv-*
The mini-viewer variant, matching the .fg-lb-mv-* classes.
Slider & carousel
--fg-arrow-*
The arrow control set.
--fg-bullet-*
The pager bullet set.
--fg-thumbs-spacing
Spacing in the thumbnail row.
--fg-viewer-*
The image-viewer layout set.
Variable
What it controls
Pagination
--fg-pagination-btn-bg
Button background. Also -hover-, -active- and -focus- variants.
--fg-pagination-btn-color
Button text colour, with the same four states.
--fg-pagination-btn-border-color
Button border colour, with the same four states.
--fg-pagination-btn-radius & --fg-pagination-btn-padding
Button radius and padding.
--fg-pagination-gap & --fg-pagination-distance
Space between controls, and between the controls and the Gallery.
Filters
--fg-filter-btn-*
The filter button set, matching .fg-filter-btn.
--fg-filter-cb-*
The checkbox set, matching .fg-filter-checkbox.
--fg-filter-dropdown-* & --fg-filter-select-*
The dropdown and select sets, matching .fg-filter-dropdown-trigger.
--fg-filter-count-*
The count badge set, matching .fg-filter-count.
--fg-filter-sidebar-width
Width of the sidebar filter arrangement.
Responsive

Read the media queries FotoGrids writes

Variables are emitted per instance, and a tablet or mobile override only appears when the value actually differs from the level above. Small output, and nothing to unpick.
CSS
emitted for one render of Gallery 42

CSS

				
					#fg-42-1 {
  --fg-radius: 8px 4px;
  --fg-gap: 12px;
}
@media (max-width: 1024px) { #fg-42-1 { --fg-gap: 8px; } }
@media (max-width: 767px)  { #fg-42-1 { --fg-gap: 6px; } }

/* --fg-radius is not repeated: its tablet and mobile
   values match desktop, so nothing is emitted for it. */
				
			

Only when it differs

--fg-gap changes at both breakpoints, so it appears three times. --fg-radius is identical at every size, so it appears once. That is the whole rule — the render layer compares each value against the level above and writes the override only on a difference.

The breakpoints are your site's own, not fixed constants. mobile_breakpoint defaults to 767 and tablet_breakpoint to 1024; both are configurable, and the filter fotogrids/render/breakpoint_config changes what the media queries use at render time.

You are not fighting the cascade

Per-render CSS is enqueued, not embedded. It lands in <head> as an ordinary stylesheet, so a theme stylesheet enqueued after it — or any rule of your own on #fg-42-1 — wins on normal specificity and source order.

There are no inline style attributes on the wrapper and no !important on the variables to work around.

Recipes

Four things you can paste in today

Each of these works on Free, in your theme or child theme stylesheet, with no plugin setting involved.

Tighten one Gallery, leave the rest alone

CSS
recipe-01.css

CSS

				
					#fg-42-1 {
    --fg-gap: 4px;
    --fg-radius: 0;
}
				
			

Instance-scoped. Every other render on the site — including the second instance of Gallery 42 on the same page — is untouched.

Restyle captions everywhere

CSS

recipe-02.css

CSS
				
					.fotogrids-collection {
    --fg-caption-overlay-bg:
      linear-gradient(transparent, rgba(0,0,0,.5));
    --fg-caption-title-color: #fff;
    --fg-caption-title-lines: 1;
    --fg-caption-align: left;
}
				
			
One rule on the umbrella class reaches every Gallery and every Album on the site, in every builder.

Style one layout only

CSS

recipe-03.css

CSS
				
					.fotogrids-collection[data-fg-layout="justified"] {
    --fg-justified-row-height: 260px;
}

.fotogrids-collection[data-fg-layout="justified"]
  .fg-item[data-fg-media-state="loading"] {
    background: #eef0f7;
}
				
			
Attribute selectors, not layout classes — and data-fg-media-state gives you a placeholder colour that disappears the moment the image loads.

Stagger items as each Gallery initialises

JS

recipe-04.js

JS

				
					document.addEventListener( 'fotogrids:gallery_initialized', ( e ) => {
  const { galleryElement, galleryId } = e.detail;
  galleryElement.querySelectorAll( '.fg-item' ).forEach( ( item, i ) => {
    item.style.setProperty( '--stagger', `${ i * 40 }ms` );
  } );
} );
				
			

A native CustomEvent on document.

No wp.hooks, no jQuery, no build step.

The Toolkit

Handles, templates, hooks, events

Per-render CSS is enqueued, not embedded. Each render registers a src-less handle fotogrids-inline-{n} and attaches its CSS with wp_add_inline_style(), so FotoGrids’ variables land in <head> as an ordinary stylesheet and a theme sheet enqueued after it wins on the normal cascade. No inline styles on the wrapper, no !important to work around.
PHP
functions.php

PHP

				
					add_action( 'wp_enqueue_scripts', function () {
    wp_enqueue_style(
        'my-gallery-overrides',
        get_stylesheet_directory_uri() . '/fotogrids.css',
        array( 'fotogrids-render-base' ),  // load after FotoGrids' base
        '1.0.0'
    );
}, 20 );
				
			

Asset handles you can depend on

fotogrids-runtime is the dependency root for nearly all FotoGrids JavaScript. fotogrids-render-base ships collection-base.css and every layout requires it.

Handles compose the way you would expect: fotogrids-image-zoom depends on fotogrids-lightbox-mini, and fotogrids-video-lightbox-mini depends on both fotogrids-runtime and fotogrids-lightbox-mini.

Depend on one, replace one, or dequeue one.

Theme template overrides

Drop fotogrids/single-fotogrids_gallery.php, fotogrids/single-fotogrids_album.php or the fotogrids/single.php fallback into your theme.

The filter fotogrids/view/template resolves the path.

The shell renders under <body id="fotogrids-view"> with .fotogrids-view__header, __body, __footer and __notice.

Styling hooks and events

Add classes with fotogrids/render/wrapper_css_classes, mutate the variable map before it is serialised with fotogrids/render/layout/style_vars, change the attributes with fotogrids/render/layout/wrapper_attrs, and move the breakpoints with fotogrids/render/breakpoint_config.

Also fotogrids/render/should_inline_assets, fotogrids/render/late_assets and fotogrids/view/body_classes.

The full list lives on Hooks & Filters.

Native JavaScript events

fotogrids:ready fires on document.

fotogrids:gallery_initialized carries { galleryElement, galleryId, kind, instance }.

Then fotogrids:lightbox:open, :close, :navigate, plus fotogrids:page_changed, fotogrids:items_inserted, fotogrids:filters_changed and fotogrids:share.

Standard CustomEvents throughout.

Frequently asked questions

No. Every id, class, attribute and custom property on this page is in Free. The Pro feature is an editor inside the product for keeping the code — the styling surface itself is open now.

Use the instance id — #fg-42-1, where 42 is the collection id and the suffix is a 1-based per-request counter. Two instances of the same Gallery on one page get their own ids. Use [data-fg-gallery-id="42"] when you want every render of that Gallery wherever it appears.

Not on specificity. Per-render CSS is enqueued as an ordinary stylesheet through wp_add_inline_style(), not written inline on the wrapper, and the variables carry no !important. A theme sheet enqueued after fotogrids-render-base, or any #fg-… rule of your own, wins normally.

The standalone View Page shell, yes — fotogrids/single-fotogrids_gallery.php, single-fotogrids_album.php or single.php in your theme, resolved through fotogrids/view/template. That is the shell only, and only in standalone mode. Item and Gallery markup is built in PHP, so it changes through hooks.

Listen for fotogrids:gallery_initialized on document. The detail carries galleryElement, galleryId, kind and instance. For content that arrives later, fotogrids:items_inserted and fotogrids:page_changed fire on the Gallery element.