Skip to content

Instantly share code, notes, and snippets.

@AlbyIanna
Last active August 5, 2025 17:25
Show Gist options
  • Select an option

  • Save AlbyIanna/2cc2c93386e5b508cb59e1efaf79258b to your computer and use it in GitHub Desktop.

Select an option

Save AlbyIanna/2cc2c93386e5b508cb59e1efaf79258b to your computer and use it in GitHub Desktop.
Obsidian DataviewJS snippet for displaying Excalidraw icons in a responsive grid.
```dataviewjs
// Get pages and sort alphabetically by filename
const pages = dv.pages("#excalidraw")
  .where(p => p.file.name.includes("icon -"))
  .sort(p => p.file.name);

// Apply grid styles to the container
dv.container.style.display = "grid";
dv.container.style.gridTemplateColumns = "repeat(auto-fill, minmax(160px, 1fr))";
dv.container.style.gap = "16px";
dv.container.style.padding = "12px";

// Add each image as a grid item
for (let page of pages) {
  const cleanName = page.file.name.replace("icon - ", "").replace(".excalidraw", "");
  
  // Create button container instead of div
  const itemButton = dv.el("button", "", {
    attr: {
      type: "button",
      style: `
        display: flex;
        flex-direction: column;
        align-items: center;
        padding: 16px;
        border-radius: 12px;
        transition: all 0.2s ease;
        cursor: pointer;
        background: var(--background-primary);
        border: 1px solid var(--background-modifier-border);
        color: inherit;
        font-family: inherit;
        width: 100%;
        min-height: 140px;
      `
    }
  });
  
  // Create image container with fixed dimensions
  const imageContainer = dv.el("div", "", {
    container: itemButton,
    parent: itemButton,
    attr: {
      style: `
        width: 80px;
        height: 80px;
        display: flex;
        justify-content: center;
        align-items: center;
        margin-bottom: 10px;
        overflow: hidden;
        border-radius: 8px;
        background: var(--background-secondary);
      `
    }
  });
  
  // Use dv.paragraph to render the wikilink properly with consistent sizing
  dv.paragraph(`![[${page.file.name}|80]]`, {
    container: imageContainer
  });
  
  // Add text label
  dv.el("div", cleanName, {
    parent: itemButton,
    attr: {
      style: `
        font-size: 0.8em;
        text-align: center;
        color: var(--text-muted);
        word-break: break-word;
        line-height: 1.3;
        max-width: 100%;
        font-weight: 500;
      `
    },
    container: itemButton
  });
  
  // Add enhanced hover and focus effects
  itemButton.addEventListener('mouseenter', () => {
    itemButton.style.transform = 'translateY(-3px) scale(1.02)';
    itemButton.style.boxShadow = '0 8px 25px rgba(0,0,0,0.15)';
    itemButton.style.borderColor = 'var(--interactive-accent)';
    itemButton.style.background = 'var(--background-secondary)';
  });
  
  itemButton.addEventListener('mouseleave', () => {
    itemButton.style.transform = 'translateY(0) scale(1)';
    itemButton.style.boxShadow = 'none';
    itemButton.style.borderColor = 'var(--background-modifier-border)';
    itemButton.style.background = 'var(--background-primary)';
  });
  
  itemButton.addEventListener('focus', () => {
    itemButton.style.outline = '2px solid var(--interactive-accent)';
    itemButton.style.outlineOffset = '2px';
  });
  
  itemButton.addEventListener('blur', () => {
    itemButton.style.outline = 'none';
  });
  
  // Handle click and keyboard navigation
  const openFile = () => {
    app.workspace.openLinkText(page.file.path, '');
  };
  
  itemButton.addEventListener('click', openFile);
  itemButton.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      openFile();
    }
  });
}

I've created this code snippet in dataviewjs (DataView plugin for Obsidian) for displaying my icon library in a nice way - built using the Excalidraw plugin by Zsolt Viczian.

Essentially, this snippet displays all #excalidraw files with names starting in "icon -" as a responsive grid of clickable image buttons. Each item shows a thumbnail preview and a label. Includes keyboard accessibility, hover/focus styles, and opens the file on click or key press. I also tested it on mobile and it works super well ✨

Here's how it looks on different screen sizes 👇

Large

image

Medium

image

Small

image

How it works

🔍 1. Query and Sort Pages

const pages = dv.pages("#excalidraw")
  .where(p => p.file.name.includes("icon -"))
  .sort(p => p.file.name);
  • Filters all notes tagged with #excalidraw and filenames containing "icon -".
  • Sorts them alphabetically by filename to maintain consistent grid order.

🧱 2. Set Up Grid Layout

dv.container.style.display = "grid";
dv.container.style.gridTemplateColumns = "repeat(auto-fill, minmax(160px, 1fr))";
dv.container.style.gap = "16px";
dv.container.style.padding = "12px";
  • Applies a responsive CSS grid to the main container.
  • Ensures the layout adapts to different screen sizes while maintaining a consistent gap and padding.

🧩 3. Render Each Grid Item

Inside the for...of loop, each page becomes a styled, interactive grid item:

a. Create a Button Container

const itemButton = dv.el("button", "", { ... });
  • Acts as the main wrapper per item.
  • Styled like a card with padding, border, and hover effects.

b. Add Image Container

const imageContainer = dv.el("div", "", { ... });
dv.paragraph(`![[${page.file.name}|80]]`, { container: imageContainer });
  • Displays a thumbnail preview of the Excalidraw file using Obsidian's ![[wikilink|80]] syntax.
  • Keeps the image centered and cropped in a fixed-size square.

c. Add Label

dv.el("div", cleanName, { ... });
  • Shows a readable label (filename without prefix/suffix).
  • Styled for clarity and visual hierarchy.

✨ 4. Add Interactive Styles

itemButton.addEventListener('mouseenter', () => { ... });
itemButton.addEventListener('mouseleave', () => { ... });
itemButton.addEventListener('focus', () => { ... });
itemButton.addEventListener('blur', () => { ... });
  • Adds smooth transitions and shadows on hover.
  • Enhances keyboard focus visibility for accessibility.

🖱️ 5. Enable Navigation

const openFile = () => {
  app.workspace.openLinkText(page.file.path, '');
};
itemButton.addEventListener('click', openFile);
itemButton.addEventListener('keydown', (e) => { ... });
  • Opens the linked note when clicked or when pressing Enter or Space.
  • Makes the grid navigable via mouse and keyboard.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment