
{"id":45881,"date":"2026-05-21T14:52:53","date_gmt":"2026-05-21T21:52:53","guid":{"rendered":"https:\/\/dramaticarts.usc.edu\/?p=45881"},"modified":"2026-05-27T14:39:20","modified_gmt":"2026-05-27T21:39:20","slug":"2026-commencment-photo-gallery","status":"publish","type":"post","link":"https:\/\/dramaticarts.usc.edu\/2026-commencment-photo-gallery\/","title":{"rendered":"2026 Commencment Photo Gallery"},"content":{"rendered":"\n<p>On May 15, 2026, the USC School of Dramatic Arts&#8217; Class of 2026 celebrated commencement with their families, friends and faculty. <\/p>\n\n\n\n<p>Photos by Luis Luque\/Capture Imaging and Grad Images.<\/p>\n\n\n\n<div id=\"sda-commencement-gallery\" style=\"border: 2px solid #990000; padding: 20px; border-radius: 8px; background: #f9f9f9; max-width: 1600px;\">\n  <h3 style=\"margin-top: 0; color: #990000;\">\ud83d\udcf8 2026 SDA Commencement<\/h3>\n\n  <p style=\"margin-bottom: 15px; font-size: 16px;\">\n    <strong>Use the arrow buttons, arrow keys, or thumbnail buttons to move through the slideshow.<\/strong>\n  <\/p>\n\n  <div\n    id=\"flickr-gallery-app\"\n    tabindex=\"0\"\n    role=\"region\"\n    aria-label=\"2026 SDA Commencement photo slideshow\"\n    style=\"outline: none;\"\n  >\n    <div id=\"gallery-loading\" style=\"display:flex; align-items:center; gap:12px; min-height: 360px;\">\n      <div class=\"spinner\" aria-hidden=\"true\"><\/div>\n      <span>Loading photos from Flickr&#8230;<\/span>\n    <\/div>\n\n    <div id=\"gallery-frame\" style=\"display:none;\">\n      <div style=\"position: relative; background:#000; border-radius: 10px; overflow:hidden;\">\n        <div id=\"slide-track\" style=\"position: relative; width: 100%; aspect-ratio: 16 \/ 9; background:#111;\"><\/div>\n\n        <button\n          id=\"prev-btn\"\n          type=\"button\"\n          aria-label=\"Previous photo\"\n          style=\"position:absolute; left:12px; top:50%; transform:translateY(-50%); width:44px; height:44px; border:none; border-radius:999px; background:rgba(255,255,255,.92); font-size:28px; line-height:44px; cursor:pointer; z-index:3;\"\n        >\u2039<\/button>\n\n        <button\n          id=\"next-btn\"\n          type=\"button\"\n          aria-label=\"Next photo\"\n          style=\"position:absolute; right:12px; top:50%; transform:translateY(-50%); width:44px; height:44px; border:none; border-radius:999px; background:rgba(255,255,255,.92); font-size:28px; line-height:44px; cursor:pointer; z-index:3;\"\n        >\u203a<\/button>\n      <\/div>\n\n      <div style=\"display:flex; justify-content:space-between; gap:12px; align-items:center; margin-top:12px; flex-wrap:wrap;\">\n        <p id=\"caption\" style=\"margin:0; font-size: 15px; color:#333;\"><\/p>\n        <p id=\"counter\" style=\"margin:0; font-size: 14px; color:#666;\"><\/p>\n      <\/div>\n\n      <div\n        id=\"thumb-strip\"\n        style=\"display:flex; gap:10px; margin-top:14px; overflow-x:auto; padding-bottom:6px;\"\n        aria-label=\"Thumbnail navigation\"\n      ><\/div>\n    <\/div>\n\n    <div id=\"gallery-status\" class=\"sr-only\" aria-live=\"polite\"><\/div>\n  <\/div>\n\n  <p style=\"margin-bottom: 0; margin-top: 10px; font-size: 14px; color: #666;\">\n    \ud83d\udca1 <em>Tip: Focus the gallery and use \u2190 \u2192 to move between photos. You can also activate any thumbnail with Enter or Space.<\/em>\n  <\/p>\n<\/div>\n\n<style>\n  .spinner {\n    width: 34px;\n    height: 34px;\n    border: 4px solid #e8e8e8;\n    border-top-color: #990000;\n    border-radius: 50%;\n    animation: spin 1s linear infinite;\n    flex: 0 0 auto;\n  }\n\n  @keyframes spin {\n    to { transform: rotate(360deg); }\n  }\n\n  .sr-only {\n    position: absolute;\n    width: 1px;\n    height: 1px;\n    padding: 0;\n    margin: -1px;\n    overflow: hidden;\n    clip: rect(0, 0, 0, 0);\n    white-space: nowrap;\n    border: 0;\n  }\n\n  .gallery-slide {\n    position: absolute;\n    inset: 0;\n    opacity: 0;\n    transition: opacity 0.35s ease;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    background: #111;\n  }\n\n  .gallery-slide.active {\n    opacity: 1;\n    z-index: 1;\n  }\n\n  .gallery-slide img {\n    width: 100%;\n    height: 100%;\n    object-fit: contain;\n    display: block;\n    background: #111;\n  }\n\n  .thumb-btn {\n    flex: 0 0 auto;\n    width: 84px;\n    height: 84px;\n    border: 3px solid transparent;\n    border-radius: 8px;\n    padding: 0;\n    overflow: hidden;\n    background: #fff;\n    cursor: pointer;\n    opacity: 0.75;\n    transition: transform 0.2s ease, opacity 0.2s ease, border-color 0.2s ease;\n  }\n\n  .thumb-btn:hover {\n    opacity: 1;\n    transform: scale(1.03);\n  }\n\n  .thumb-btn:focus {\n    outline: 3px solid #990000;\n    outline-offset: 2px;\n  }\n\n  .thumb-btn.active {\n    opacity: 1;\n    border-color: #990000;\n  }\n\n  .thumb-btn img {\n    width: 100%;\n    height: 100%;\n    object-fit: cover;\n    display: block;\n  }\n<\/style>\n\n<script>\n(function () {\n  const config = {\n    apiKey: \"1f1f5816a08923a283bd7db27b66d58f\",\n    albumId: \"72177720333776388\",\n    title: \"2026 SDA Commencement\"\n  };\n\n  const root = document.getElementById(\"flickr-gallery-app\");\n  const loadingEl = document.getElementById(\"gallery-loading\");\n  const frameEl = document.getElementById(\"gallery-frame\");\n  const slideTrackEl = document.getElementById(\"slide-track\");\n  const prevBtn = document.getElementById(\"prev-btn\");\n  const nextBtn = document.getElementById(\"next-btn\");\n  const captionEl = document.getElementById(\"caption\");\n  const counterEl = document.getElementById(\"counter\");\n  const thumbStripEl = document.getElementById(\"thumb-strip\");\n  const statusEl = document.getElementById(\"gallery-status\");\n\n  const state = {\n    photos: [],\n    index: 0\n  };\n\n  function escapeHtml(text) {\n    const div = document.createElement(\"div\");\n    div.textContent = text == null ? \"\" : String(text);\n    return div.innerHTML;\n  }\n\n  function jsonp(url) {\n    return new Promise((resolve, reject) => {\n      const callbackName = \"flickr_cb_\" + Math.random().toString(36).slice(2);\n      const script = document.createElement(\"script\");\n\n      window[callbackName] = function (data) {\n        cleanup();\n        resolve(data);\n      };\n\n      function cleanup() {\n        if (script.parentNode) script.parentNode.removeChild(script);\n        try { delete window[callbackName]; } catch (e) { window[callbackName] = undefined; }\n      }\n\n      script.onerror = function () {\n        cleanup();\n        reject(new Error(\"Could not load Flickr data.\"));\n      };\n\n      script.src = url + \"&jsoncallback=\" + callbackName;\n      document.body.appendChild(script);\n    });\n  }\n\n  function bestUrl(photo) {\n    return (\n      photo.url_o ||\n      photo.url_k ||\n      photo.url_h ||\n      photo.url_l ||\n      photo.url_c ||\n      photo.url_z ||\n      photo.url_m ||\n      (\"https:\/\/live.staticflickr.com\/\" + photo.server + \"\/\" + photo.id + \"_\" + photo.secret + \"_b.jpg\")\n    );\n  }\n\n  function thumbUrl(photo) {\n    return photo.url_q ||\n      (\"https:\/\/live.staticflickr.com\/\" + photo.server + \"\/\" + photo.id + \"_\" + photo.secret + \"_q.jpg\");\n  }\n\n  async function loadPhotos() {\n    const url =\n      \"https:\/\/api.flickr.com\/services\/rest\/\" +\n      \"?method=flickr.photosets.getPhotos\" +\n      \"&api_key=\" + encodeURIComponent(config.apiKey) +\n      \"&photoset_id=\" + encodeURIComponent(config.albumId) +\n      \"&extras=description,url_o,url_k,url_h,url_l,url_c,url_z,url_m,url_q\" +\n      \"&format=json\";\n\n    const data = await jsonp(url);\n\n    if (!data || data.stat !== \"ok\" || !data.photoset || !Array.isArray(data.photoset.photo)) {\n      throw new Error(\"Flickr returned an unexpected response.\");\n    }\n\n    state.photos = data.photoset.photo.map((photo, idx) => {\n      const title = (photo.title && photo.title.trim()) ? photo.title.trim() : `Photo ${idx + 1}`;\n      return {\n        id: photo.id,\n        title,\n        description: photo.description && photo.description._content ? photo.description._content : \"\",\n        image: bestUrl(photo),\n        thumb: thumbUrl(photo),\n        alt: `${title} from ${config.title}`\n      };\n    });\n  }\n\n  function renderSlides() {\n    slideTrackEl.innerHTML = state.photos.map((photo, idx) => `\n      <figure class=\"gallery-slide ${idx === state.index ? \"active\" : \"\"}\" data-index=\"${idx}\" aria-roledescription=\"slide\" aria-label=\"${idx + 1} of ${state.photos.length}\">\n        <img decoding=\"async\"\n          src=\"${escapeHtml(photo.image)}\"\n          alt=\"${escapeHtml(photo.alt)}\"\n          ${idx === 0 ? 'loading=\"eager\"' : 'loading=\"lazy\"'}\n        \/>\n      <\/figure>\n    `).join(\"\");\n\n    thumbStripEl.innerHTML = state.photos.map((photo, idx) => `\n      <button\n        type=\"button\"\n        class=\"thumb-btn ${idx === state.index ? \"active\" : \"\"}\"\n        data-index=\"${idx}\"\n        aria-label=\"Go to photo ${idx + 1}: ${escapeHtml(photo.title)}\"\n      >\n        <img decoding=\"async\" src=\"${escapeHtml(photo.thumb)}\" alt=\"\" aria-hidden=\"true\">\n      <\/button>\n    `).join(\"\");\n\n    thumbStripEl.querySelectorAll(\".thumb-btn\").forEach(btn => {\n      btn.addEventListener(\"click\", () => goToSlide(Number(btn.dataset.index)));\n      btn.addEventListener(\"keydown\", (e) => {\n        if (e.key === \"Enter\" || e.key === \" \") {\n          e.preventDefault();\n          goToSlide(Number(btn.dataset.index));\n        }\n      });\n    });\n\n    updateUI();\n    loadingEl.style.display = \"none\";\n    frameEl.style.display = \"block\";\n    announce(`Loaded ${state.photos.length} photos.`);\n  }\n\n  function updateUI() {\n    const slides = slideTrackEl.querySelectorAll(\".gallery-slide\");\n    const thumbs = thumbStripEl.querySelectorAll(\".thumb-btn\");\n\n    slides.forEach((slide, idx) => slide.classList.toggle(\"active\", idx === state.index));\n    thumbs.forEach((thumb, idx) => thumb.classList.toggle(\"active\", idx === state.index));\n\n    const current = state.photos[state.index];\n    captionEl.textContent = current ? current.title : \"\";\n    counterEl.textContent = state.photos.length ? `${state.index + 1} \/ ${state.photos.length}` : \"\";\n\n    if (thumbs[state.index]) {\n      thumbs[state.index].scrollIntoView({ behavior: \"smooth\", inline: \"center\", block: \"nearest\" });\n    }\n  }\n\n  function goToSlide(nextIndex) {\n    if (!state.photos.length) return;\n    const total = state.photos.length;\n    state.index = (nextIndex + total) % total;\n    updateUI();\n\n    const current = state.photos[state.index];\n    announce(`Showing photo ${state.index + 1} of ${total}. ${current ? current.title : \"\"}`);\n  }\n\n  function nextSlide() {\n    goToSlide(state.index + 1);\n  }\n\n  function prevSlide() {\n    goToSlide(state.index - 1);\n  }\n\n  function announce(message) {\n    statusEl.textContent = message;\n  }\n\n  prevBtn.addEventListener(\"click\", prevSlide);\n  nextBtn.addEventListener(\"click\", nextSlide);\n\n  root.addEventListener(\"keydown\", (e) => {\n    if (!root.contains(document.activeElement)) return;\n    if (e.key === \"ArrowLeft\") {\n      e.preventDefault();\n      prevSlide();\n    }\n    if (e.key === \"ArrowRight\") {\n      e.preventDefault();\n      nextSlide();\n    }\n  });\n\n  loadPhotos()\n    .then(renderSlides)\n    .catch((err) => {\n      loadingEl.innerHTML = '<div style=\"color:#b00020;\"><strong>Could not load the slideshow.<\/strong> ' + escapeHtml(err.message) + \"<\/div>\";\n    });\n})();\n<\/script>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Photos from the 2026 SDA Commencement Ceremony.<\/p>\n","protected":false},"author":6,"featured_media":45884,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"categories":[11],"tags":[],"class_list":["post-45881","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-news"],"acf":[],"_links":{"self":[{"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/posts\/45881","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/comments?post=45881"}],"version-history":[{"count":2,"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/posts\/45881\/revisions"}],"predecessor-version":[{"id":45896,"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/posts\/45881\/revisions\/45896"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/media\/45884"}],"wp:attachment":[{"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/media?parent=45881"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/categories?post=45881"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dramaticarts.usc.edu\/wp-json\/wp\/v2\/tags?post=45881"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}