Browse Source

side bar menu styles

master
rdrew 2 weeks ago
parent
commit
e92ffbef0b
  1. 9
      css/base.css
  2. 50
      css/components/menu.css
  3. 122
      css/components/unsorted.css
  4. 23
      css/layout.css
  5. 2
      druid.libraries.yml
  6. 25
      druid.theme
  7. 86
      js/druid.behaviors.js

9
css/base.css

@ -31,18 +31,19 @@
}
h1 {
font-size: var(--font-size-6);
font-weight: normal;
}
h2 {
font-size: var(--font-size-5);
font-size: var(--font-size-4);
}
h3 {
font-size: var(--font-size-4);
font-size: var(--font-size-3);
}
h4 {
font-size: var(--font-size-3);
font-size: var(--font-size-2);
}
h5 {
font-size: var(--font-size-2);
font-size: var(--font-size-1);
}
h6 {
font-size: var(--font-size-1);

50
css/components/menu.css

@ -1,5 +1,4 @@
@layer components {
/**
* @file
* Visual styles for menu.
@ -34,5 +33,54 @@ ul.menu {
ul.menu a.is-active {
color: #000;
}
/* dropdown behaviours */
/* add the region with a menu in it to convert to a dropdown */
/* check the menu js behaviour when adding new dropdowns */
header {
.menu {
display: flex;
}
.menu ul {
list-style: none;
margin: 0;
padding: 0;
}
.menu > ul {
display: flex;
gap: 1rem;
}
.menu li {
position: relative;
}
/* Hide submenus */
.menu li ul {
position: absolute;
top: 100%;
left: 0;
display: none;
min-width: 200px;
background: white;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
/* Show submenu on hover */
.menu li:hover > ul {
display: block;
}
/* Optional styling */
.menu a {
display: block;
padding: 0.75rem 1rem;
text-decoration: none;
}
.menu li ul li a {
padding: 0.5rem 1rem;
}
}
}

122
css/components/unsorted.css

@ -0,0 +1,122 @@
@layer components {
/* ======================================= */
/* sidebar menu styles */
/* ======================================= */
/* sliding yellow links, no dropdowns */
.null.region-sidebar-second {
.menu {
margin: 0;
}
.menu a {
text-decoration: none;
display: block;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAARCAYAAADkIz3lAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAh5JREFUeNpi/PPnz//Xb98xMDMy7mFgYHBlgAImZiYGZMB0++59Bke/SIbKlm4XBkbGbUDMAMZogOnps+cv//3/y7Bq0zaGsoZ2z1+/fq4DYkyFP3/+MK0vyXkiLS7KsGL9Vob6zomBzMzMK4BybMgKmSMiIj+xs7JuNtbX8bl9/5Hg7oNHGd6+fa9jY26szMbKugvoiF8ghzAnpaQwsLKyvuPj5dmtpa7scvPWPRGQ4u8/f+o62FjIsbOz7wPa8JM5OjqG4f+/fyD8hpeba7++jqbdw8fPxDbv3sfw4eMnPUtTQ1mgwgPM0bFxyE55zcPNdVhJXsby0ZPnkrsPHmP4+vWbnrG+rjzDi+fPsOHSurae/yxiKv8ZBOX+r1y/+T8Lv6AgekgY3bn3MPHYmQsMf/78ZvB1d2bgZGWZy/Dp0ydkbHD2wuXbdj5h/xmEFf4Hxqb+37Zrz+xde/fzsfz+8wdmku7Dx083FtW2yB0+fprB3cGGISLAa/avnz8LfjEwfGOBRpbGo6fP9uRWNoidPHORwdfDmSEjIWIuEyNjNlDuNzRmfik8ePzsWHZZvdi5i1cZnG3MGWKDfBZ9/fQlBaYIBFjevv9wMbO0hu/ajdsMTjYWDPnpCauA4vEgyX///sJ9yPLp02e+p89eMFgY6zMkRwZv+vLpczhMkouHC66Q8efPn/9v3r7L8PPXr+1AvhdyOL199xbOBggwAPE+9T0FxGv+AAAAAElFTkSuQmCC);
background-position: 96% 10px;
background-repeat: no-repeat;
padding: 6px 20px 6px 0;
background-color: transparent;
color: #192a36;
/* display: inline-block; */
/* padding: 0 3px; */
margin-left: 0;
margin-right: -3px;
position: relative;
text-decoration: none;
transition: color ease 0.3s;
z-index: 0;
}
li.menu-item {
border-bottom: 2px solid var(--upei-orange);
font-size: 16px;
padding-top: 0;
a:before {
content: "";
position: absolute;
z-index: -1;
width: 0;
height: 100%;
left: 0;
bottom: 0;
background-color: var(--upei-orange);
transition: all ease 0.3s;
}
a:hover:before {
width: 100%;
}
}
}
/* sidebar block menu with dropdowns (may need to adjust .block-menu and/or regions) */
/* *see the associated behavior */
.region-sidebar-second {
nav h2 {
margin-bottom: 0;
}
nav ul.menu li a {
text-decoration: none;
padding: 7px 0 7px 10px;
display: block;
border-bottom: 2px solid #8c2004;
color: #192a36;
}
.menu {
margin: 0;
/* margin-bottom: 1rem; */
}
.menu-item {
padding: 0;
}
.menu-item--expanded .menu a {
margin-left: 1em;
border: none;
}
/* Hide nested menus by default */
.block-menu .menu-item--expanded > ul {
display: none;
}
/* Show when open */
.block-menu .menu-item--expanded.open > ul {
display: block;
}
/* Position container */
.block-menu .menu-item--expanded {
position: relative;
list-style-type: none;
list-style-image: none;
}
/* Caret button */
.block-menu .menu-caret {
position: absolute;
right: 0.5rem;
top: 0.75rem;
cursor: pointer;
font-size: 0.8rem;
user-select: none;
}
/* Optional caret icon (triangle) */
.block-menu .menu-caret::after {
content: "▼";
}
/* Rotate when open */
.block-menu .menu-item--expanded.open > .menu-caret::after {
transform: rotate(180deg);
display: inline-block;
}
.block-menu .menu-item--expanded > ul {
display: block;
max-height: 0;
overflow: hidden;
transition: max-height 0.5s ease;
}
.block-menu .menu-item--expanded.open > ul {
max-height: 500px;
}
}
}

23
css/layout.css

@ -18,7 +18,6 @@
* responsiveness see todo.md for the rationale.
*/
@layer layout {
.content-rail {
max-width: var(--content-max-width);
margin-inline: auto;
@ -36,6 +35,7 @@
padding-block: var(--size-3);
}
.region-header,
.top-nav__inner {
display: flex;
justify-content: space-between;
@ -65,7 +65,9 @@
No-sidebar pages skip the grid entirely; .layout-content just fills
its parent rail. */
body:is(.layout-sidebar-first, .layout-sidebar-second, .layout-two-sidebars) main > .content-rail {
body:is(.layout-sidebar-first, .layout-sidebar-second, .layout-two-sidebars)
main
> .content-rail {
display: grid;
gap: var(--size-4);
grid-template-columns: minmax(0, 1fr);
@ -81,9 +83,15 @@
grid-template-areas: "content" "first" "second";
}
.layout-content { grid-area: content; }
.layout-sidebar-first { grid-area: first; }
.layout-sidebar-second { grid-area: second; }
.layout-content {
grid-area: content;
}
.layout-sidebar-first {
grid-area: first;
}
.layout-sidebar-second {
grid-area: second;
}
/* Tablet+: single-sidebar layouts side-by-side. */
@media (min-width: 768px) {
@ -100,9 +108,10 @@
/* Small-desktop+: two-sidebar layout becomes three columns. */
@media (min-width: 1024px) {
body.layout-two-sidebars main > .content-rail {
grid-template-columns: var(--sidebar-width) minmax(0, 1fr) var(--sidebar-width);
grid-template-columns: var(--sidebar-width) minmax(0, 1fr) var(
--sidebar-width
);
grid-template-areas: "first content second";
}
}
}

2
druid.libraries.yml

@ -122,6 +122,8 @@ components:
weight: -10
css/components/ui-dialog.css:
weight: -10
css/components/unsorted.css:
weight: -10
dialog:
version: VERSION
css:

25
druid.theme

@ -15,22 +15,20 @@
* - layout-one-sidebar + layout-sidebar-second (only sidebar_second has content)
* - layout-two-sidebars
*/
function druid_preprocess_html(array &$variables): void {
function druid_preprocess_html(array &$variables): void
{
$has_first = !empty($variables['page']['sidebar_first']);
$has_second = !empty($variables['page']['sidebar_second']);
if ($has_first && $has_second) {
$variables['attributes']['class'][] = 'layout-two-sidebars';
}
elseif ($has_first) {
} elseif ($has_first) {
$variables['attributes']['class'][] = 'layout-one-sidebar';
$variables['attributes']['class'][] = 'layout-sidebar-first';
}
elseif ($has_second) {
} elseif ($has_second) {
$variables['attributes']['class'][] = 'layout-one-sidebar';
$variables['attributes']['class'][] = 'layout-sidebar-second';
}
else {
} else {
$variables['attributes']['class'][] = 'layout-no-sidebars';
}
}
@ -38,14 +36,23 @@ function druid_preprocess_html(array &$variables): void {
/**
* Implements hook_preprocess_image_widget().
*/
function druid_preprocess_image_widget(array &$variables): void {
function druid_preprocess_image_widget(array &$variables): void
{
$data = &$variables['data'];
// This prevents image widget templates from rendering preview container HTML
// to users that do not have permission to access these previews.
// @todo revisit in https://drupal.org/node/953034
// @todo revisit in https://drupal.org/node/3114318
if (isset($data['preview']['#access']) && $data['preview']['#access'] === FALSE) {
if (isset($data['preview']['#access']) && $data['preview']['#access'] === false) {
unset($data['preview']);
}
}
function druid_preprocess_menu(&$variables)
{
foreach ($variables['items'] as &$item) {
if (!empty($item['below'])) {
$item['attributes']->addClass('has-dropdown');
}
}
}

86
js/druid.behaviors.js

@ -68,4 +68,90 @@
// });
// },
// };
Drupal.behaviors.mainMenuToggle = {
attach(context) {
once(
"dropdown-menu-toggle",
"nav .menu li.menu-item--expanded > a",
context
).forEach((link) => {
let touched = false;
const handler = function (e) {
// Prevent duplicate firing (touch + click)
if (e.type === "click" && touched) {
touched = false;
return;
}
if (e.type === "touchstart") {
touched = true;
}
const parent = this.parentElement;
const submenu = parent.querySelector("ul");
// Only toggle if submenu exists
if (submenu) {
e.preventDefault();
parent.classList.toggle("open");
}
};
// Mobile-first interaction
link.addEventListener("touchstart", handler, { passive: true });
// Desktop + accessibility fallback
link.addEventListener("click", handler);
});
},
};
})(Drupal, once);
((Drupal, once) => {
Drupal.behaviors.sidebarMenuDropdown = {
attach(context) {
once(
"sidebar-menu-dropdown",
".block-menu .menu-item--expanded",
context
).forEach((item) => {
const link = item.querySelector("a");
const submenu = item.querySelector("ul");
if (!submenu || !link) return;
// ✅ Create caret element
const caret = document.createElement("span");
caret.className = "menu-caret";
caret.setAttribute("aria-expanded", "false");
// Insert caret after link
link.insertAdjacentElement("afterend", caret);
let touched = false;
const toggleMenu = (e) => {
// Prevent touch + click double fire
if (e.type === "click" && touched) {
touched = false;
return;
}
if (e.type === "touchstart") {
touched = true;
}
e.preventDefault();
e.stopPropagation();
const isOpen = item.classList.toggle("open");
caret.setAttribute("aria-expanded", isOpen);
};
// ✅ Only caret toggles menu (link still navigates)
caret.addEventListener("touchstart", toggleMenu, { passive: false });
caret.addEventListener("click", toggleMenu);
});
},
};
})(Drupal, once);

Loading…
Cancel
Save