Reacting when N videos have preloaded a given time interval?

Is there a way to react ( Promise?) once 2 seconds of 5 videos has been preloaded?

I was watching a youtube where the content creator used GSAP to animate the switching between videos, which was very smooth, and I’d like to create something similar, but with a spinner that is shown until 2 seconds of each video has been loaded, that way the experience is smooth.

How to poll with an async function without creating an endlessly deeper call stack

I’m using Svelte. For a dynamic real-time chart, I’m pulling data from a data source that is constantly making new data available. So I want to get the latest data, then as soon as it comes back get the latest data again, and so forth indefinitely until the user decides to stop the loop. The data source call, implemented in the database with PL/SQL, includes a sleep in order to space out the calls to a minimum of 5 seconds apart. But the javascript front-end simply wants to loop on making that database call.

My problem is that as I’ve written it, I’m getting uncontrolled call stack depth – which is quickly going to get me into trouble. Mock-up:

let loopTrigger=1

async function getData() {
  await getNewData(somethingElse) // call database here, which takes several seconds
  loopTrigger=Date.now()
}

$: loopTrigger && somethingElse && getData()

I initiate by setting somethingElse and then due to setting loopTrigger within the async function it becomes an infinite reaction loop, by design. It’s not burning CPU because the database call take a long time and handles the sleeping to space the calls out. But in javascript the call stack gets deeper and deeper. Eventually it’s going to go bonkers.

To make matters worse, if I change somethingElse (changed parameters for the database call) then it appears to initiate a second loop without stopping the original one, as if there were two reaction chains, and now there are two loops happening concurrently and apparently independent of each other, which I don’t want. I want only one loop, restarted when somethingElse changes, and repeating when the database call completes until the user cancels it. What’s the proper way to do this?

When exactly is the Global Execution Context (GEC) popped from the Call Stack in JavaScript?

I’m trying to better understand how JavaScript’s execution model works, specifically with the Global Execution Context (GEC) and how it interacts with the Call Stack, Promises, and the Event Loop.

Consider the following example:

console.log("Start");

setTimeout(() => {
 console.log("Timeout callback");
}, 0);

Promise.resolve().then(() => {
 console.log("Promise callback");
});

console.log("End");

From my understanding:

All synchronous code is executed first.

Then microtasks (like .then() callbacks) are handled.

Then callback in the callback queue (like setTimeout) are processed.

My question is:

  • When is the Global Execution Context (GEC) actually popped from the Call Stack?

  • Is it after console.log(“End”), or after all microtasks and callbacks are completed?

I’m confused because I know the event loop only runs microtasks if the call stack is empty, but at the same time, people say the GEC “remains active” after synchronous code.

Does the GEC stay on the stack while the microtask queue is being processed? Or is it popped immediately after the synchronous code completes?

Integrating New Google Maps Autocomplete API into Django Project

I’m using the new Google PlaceAutocompleteElement() API function (not the legacy google.maps.places.Autocomplete) and would like help integrating it into my Django project.

Specifically, I want to add an address search bar to my application.html template.

I’ve tried connecting the initMap() JavaScript function to the form in several ways, updating all the corresponding files below, but the form submission always fails, and the data never reaches the database.

Here’s more information about the this Google API:
Place Autocomplete Element

Take a look of my attempt before the address implementation:

models.py

class Applicant(models.Model):
    Name = models.CharField(max_length=101)
    DOB = models.DateField()
    High_School = models.CharField(max_length=100)
    Major = models.CharField(max_length=100)
    SSN = models.CharField(max_length=11)
    Phone = models.CharField(max_length=15)
    
    def __str__(self):
        return self.Name

views.py

def application_page(request):
    form = ApplicationForm()
    return render(request, "DunesApp/application.html", {
        'form': form,
        'google_maps_key': settings.GOOGLE_MAPS_API_KEY
    })

def submit_application(request):
    if request.method == "POST":
        form = ApplicationForm(request.POST)
        if form.is_valid():
            form.save()
            return JsonResponse({"status": "success"})
    return JsonResponse({"status": "error"})

forms.py

class ApplicationForm(forms.ModelForm):
    class Meta:
        model = Applicant
        fields = "__all__"

script.js (called inside layout.html)

function submitApplication() {
    const form = document.getElementById('application-form');
    const formData = new FormData(form);

    fetch('/submit_application/', {
        method: 'POST',
        body: formData,
        headers: {
            'X-CSRFToken': formData.get('csrfmiddlewaretoken')
        }
    })
    .then(response => response.json())
    .then(data => {
        const messageBox = document.getElementById('application-message');

        if (data.status === 'success') {
            messageBox.className = 'alert alert-success mt-3';
            messageBox.innerText = 'Application submitted successfully!';
            messageBox.classList.remove('d-none');
            form.reset();
        } else {
            messageBox.className = 'alert alert-danger mt-3';
            messageBox.innerText = 'Submission failed. Please try again.';
            messageBox.classList.remove('d-none');
        }
    })
    .catch(error => {
        const messageBox = document.getElementById('application-message');
        messageBox.className = 'alert alert-danger mt-3';
        messageBox.innerText = 'An error occurred. Please try again.';
        messageBox.classList.remove('d-none');
        console.error('Error:', error);
    });
}

application.html

{% extends 'DunesApp/layout.html' %}
{% load form_tags %}
{% block content %}
  <h2 class="mb-4">Student Application Form</h2>

  <div class="card p-4 shadow-sm">
    <form id="application-form">
      {% csrf_token %}

      <div class="mb-3">
        <label for="id_name" class="form-label">Full Name:</label>
        {{ form.Name|add_class:'form-control' }}
      </div>

      <div class="mb-3">
        <label for="id_dob" class="form-label">Date of Birth:</label>
        {{ form.DOB|add_class:'form-control' }}
      </div>

      <div class="mb-3">
        <label for="id_ssn" class="form-label">Social Security Number:</label>
        {{ form.SSN|add_class:'form-control' }}
      </div>

      <!-- Google Address Autocomplete will be here -->

      <div class="mb-3">
        <label for="id_phone" class="form-label">Phone Number:</label>
        {{ form.Phone|add_class:'form-control' }}
      </div>

      <div class="mb-3">
        <label for="id_high_school" class="form-label">High School:</label>
        {{ form.High_School|add_class:'form-control' }}
      </div>

      <div class="mb-3">
        <label for="id_major" class="form-label">Intended Major:</label>
        {{ form.Major|add_class:'form-select' }}
      </div>

      <button type="button" class="btn btn-primary" onclick="submitApplication()">Submit Application</button>
    </form>
    <div id="application-message" class="alert mt-3 d-none"></div>
  </div>

Desired Design

Desired Design

How to change iFrame src with a default value when the original src does not exist?

I have the following basic html code in a file called default.html:

<html>
 <head></head>
 <body>

  <input id="year" type="number" min="2000" max="2099" value="2025" onchange="getcontent(this.value)"/>
  <br>
  <iframe id="content" name="content"></iframe>

  <script>
   function getcontent(year)
   {
    var iframe = document.getElementById('content');
    iframe.src = year + ".html";
   }
  </script>

 </body>
</html>

In the same folder, I have other html files with names like 2022.html, 2025.html, etc. In addition to the previous files, I have 1 more file called Empty.html.

On my main page (Default.html), I have an input box that can have a value between 2000 and 2099 and below it, I have an iFrame which I populate based on the value of the input box by changing the src attribute of the iFrame to one of the other html files (2022.html for example if the value of the input box is 2002).

Not all html files exist for the range of value between 2000 and 2099. if the file exists it is displayed in the iFrame just fine, if the file does not exist, I get the browsers default Page not found message. What I would like to do is to display my Empty.html file instead.

Please note that I am not using a web server of any kind, these are all local files on my PC and I access them using my browser which happens to be MS-Edge.

I have searched and tried a few things to no avail mostly to me being a newbie at this 🙂
Any help is much appreciated.

Some of the articles I have gone through:

How to detect when an iframe has already been loaded

How to check if iframe is loaded or it has a content?

Checking if iframe contents are loaded?

Capture iframe load complete event

using javascript to detect whether the url exists before display in iframe

Check if a file exists locally using JavaScript only

Check if a local HTML file exists and redirect to it if it does

Check if image exists on server using JavaScript?

I am try to implemetn sticky card sroll effect

I’m trying to implement a sticky card scroll effect, but I’m running into some issues and haven’t had any luck so far.

Here are the specific problems I’m facing:

The headline “Our Workflow” does not work as intended. Here is the screenshot
enter image description here

None of the workflow cards scroll or animate — they remain static on the page.

I’ve spent the entire day troubleshooting this without success. Below is a code snippet of my current implementation (HTML, CSS, and JS).

I’d really appreciate any guidance, or if someone could share a working version of this kind of scroll effect, that would be incredibly helpful.

Thanks in advance for your support!

HTMLCode:

    <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Infinite Masta LLC - Launch Your Dream App</title>
  <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/aos.css" />
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <!-- Workflow Section -->
  <section id="workflow" class="workflow">
    <h2>Our Workflow</h2>
    <div class="workflow-sticky-container">
      <div class="workflow-sticky-content">
        <div class="workflow-card active" id="card-1">
          <div class="left-section">
            <div class="icon"><i class="fas fa-lightbulb"></i></div>
          </div>
          <div class="right-section">
            <div class="label">First</div>
            <h3>Discovery & Planning</h3>
            <p>Through in-depth discussions with stakeholders and thorough analysis, we gather all necessary requirements, conduct feasibility study, and clearly define the project scope to set a solid foundation for successful project execution.</p>
          </div>
        </div>

        <div class="workflow-card" id="card-2">
          <div class="left-section">
            <div class="icon"><i class="fa-solid fa-code icon"></i></div>
          </div>
          <div class="right-section">
            <div class="label">Then</div>
            <h3>Design & Development</h3>
            <p>Our team of expert designers and developers work together to create a user-friendly, visually appealing, and functional product that meets your specific business needs.</p>
          </div>
        </div>

        <div class="workflow-card" id="card-3">
          <div class="left-section">
            <div class="icon"><i class="fas fa-vial"></i></div>
          </div>
          <div class="right-section">
            <div class="label">And</div>
            <h3>Testing & Deployment</h3>
            <p>Before launching your product, we conduct rigorous testing to ensure that it performs flawlessly. Once everything is in order, we deploy the product and provide ongoing support to ensure its success.</p>
          </div>
        </div>

        <div class="workflow-card" id="card-4">
          <div class="left-section">
            <div class="icon"><i class="fas fa-cogs"></i></div>
          </div>
          <div class="right-section">
            <div class="label">Finally</div>
            <h3>Support & Maintenance</h3>
            <p>Our commitment doesn't end with the launch. We provide continuous support and maintenance to keep your project up-to-date, secure, and optimized. You can count on us as your reliable IT partner.</p>
          </div>
        </div>
      </div>
    </div>
  </section>

  <footer>
    &copy; 2025 Infinite Masta LLC. All rights reserved.
  </footer>

  <script src="https://unpkg.com/[email protected]/dist/aos.js"></script>
  <script src="script.js"></script>
</body>
</html>

Here is the CSS CODE:

    .workflow {
  position: relative;
  padding: 80px 20px;
  background: #0F0F1A;
  color: #fff;
  text-align: center;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.workflow h2 {
  font-size: 42px;
  color: #00FFFF;
  margin-bottom: 40px;
  position: sticky;
  top: 120px;
  z-index: 10;
  background: linear-gradient(180deg, #0F0F1A 80%, rgba(15, 15, 26, 0));
  padding: 20px 0 40px;
  margin-top: -20px;
  transition: all 0.3s ease;
}

.workflow h2.scrolled-past {
  position: relative;
  top: auto;
  background: transparent;
}

/* Sticky Scroll Container */
.workflow-sticky-container {
  position: relative;
  width: 100%;
  height: 400vh; /* 4x viewport height for 4 cards */
}

.workflow-sticky-content {
  position: sticky;
  top: 220px; /* Adjusted to account for sticky header and heading */
  height: calc(100vh - 220px);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Original Workflow Cards */
.workflow-card {
  position: absolute;
  top: 0;
  left: 50%;
  width: 90%;
  max-width: 800px;
  background: linear-gradient(145deg, #1A1A2E, #141421);
  padding: 30px;
  border-radius: 20px;
  box-shadow: 0 15px 35px rgba(0,0,0,0.3);
  display: flex;
  transform: translateX(-50%) translateY(110%);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.8s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.6s ease;
  will-change: transform, opacity;
  z-index: 1;
}

.workflow-card.active {
  transform: translateX(-50%) translateY(0%);
  opacity: 1;
  pointer-events: auto;
  z-index: 2;
}

.workflow-card .left-section {
  width: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
  border-right: 1px solid #2a2a42;
}

.workflow-card .icon {
  font-size: 100px;
  color: #00FFFF;
  margin-bottom: 20px;
}

.workflow-card .label {
  font-size: 18px;
  color: #FF00C7;
  font-weight: bold;
  text-transform: uppercase;
}

.workflow-card .right-section {
  width: 50%;
  padding: 20px;
  text-align: left;
}

.workflow-card h3 {
  font-size: 28px;
  margin: 15 0 15px;
  color: white;
}

.workflow-card p {
  font-size: 18px;
  color: #b9b9b9;
  line-height: 1.6;
  margin: 0;
}

.workflow-card:hover {
  transform: translateX(-50%) translateY(-5px);
  box-shadow: 0 20px 40px rgba(0,0,0,0.4);
}

.workflow-card.active:hover {
  transform: translateX(-50%) translateY(-5px);
}

Here is the js code

    // Sticky Card Scroll Effect for Workflow Section
document.addEventListener("DOMContentLoaded", function () {
  const workflowSection = document.querySelector("#workflow");
  const workflowHeading = document.querySelector(".workflow h2");
  const stickyContainer = document.querySelector(".workflow-sticky-container");
  const stickyContent = document.querySelector(".workflow-sticky-content");
  const cards = Array.from(document.querySelectorAll(".workflow-card"));

  // Set the height of the sticky container based on number of cards
  stickyContainer.style.height = `${cards.length * 100}vh`;

  // Initialize the first card as active
  cards.forEach((card, index) => {
    if (index === 0) {
      card.classList.add("active");
    } else {
      card.classList.remove("active");
    }
  });

  // Scroll event listener for the sticky effect
  window.addEventListener("scroll", function () {
    const workflowTop = workflowSection.offsetTop;
    const workflowHeight = workflowSection.offsetHeight;
    const scrollPosition = window.scrollY;
    const viewportHeight = window.innerHeight;

    // Check if we're in the workflow section
    if (scrollPosition >= workflowTop && scrollPosition <= workflowTop + workflowHeight) {
      // Calculate which card should be active based on scroll position
      const scrollProgress = (scrollPosition - workflowTop) / (workflowHeight - viewportHeight);
      const activeIndex = Math.min(
        Math.floor(scrollProgress * cards.length),
        cards.length - 1
      );

      // Update active card
      cards.forEach((card, index) => {
        if (index === activeIndex) {
          card.classList.add("active");
        } else {
          card.classList.remove("active");
        }
      });

      // Check if we've scrolled past the last card
      if (scrollPosition >= workflowTop + workflowHeight - viewportHeight * 0.8) {
        workflowHeading.classList.add("scrolled-past");
      } else {
        workflowHeading.classList.remove("scrolled-past");
      }
    } else {
      // Reset when not in workflow section
      workflowHeading.classList.remove("scrolled-past");
    }
  });
});

Highlight menu item with top section scrolling into view including last non-top visible sections

JSFiddle: https://jsfiddle.net/8xo4aghn/

In this Fiddle, I have a menu which highlights the item corresponding to the current top section as the page is being scrolled. The last few sections may be short (as in this example) and they never get the menu selection. It’s also possible to click the A-links to go to the section, but for the last few, the clicking won’t select the item either.

I need to dynamically force any last short sections (if they exist) to at least correspond to menu clicks, if they’ll never be activated via scrolling, so that the menu clicks make sense. Is this possible, or are there any better solutions to this scrolling problem? If I were to do it the other way – the last section being shown — then clicking any non-last sections could conflict with that as well, and there would be similar discrepancies.

function updateMenu(){

    const menus = document.querySelectorAll('div.sticky a');

    const sectionsAll = document.querySelectorAll('div.card');
    // Exclude any Sections that don't have an H2
    const sections = [...sectionsAll].filter(section => {
        const sectionH2 = section.querySelectorAll('h2');
        const sectionHeader = 
                                                (sectionH2.length ? sectionH2[0] :
                                                    null);
        if (!sectionHeader) {
            return false; //exclude
        } else {
            return true;
        }
    });

    let currentSection = null;
    for (var i = 0; i < sections.length; i++) {
        const rect = sections[i].getBoundingClientRect();
        
        // Find current section; if not last in the list, stop immediately when found
        if(rect.top >= 0) {
            currentSection = sections[i];
            if (i < sections.length - 1) {
                break;
            }
        }
  }
    
    if(currentSection){
        // Try to find a matching menu based on the A innerText
        menus.forEach(menu => {
            const menuInnerText = menu.innerText;
        
            const currentSectionH2 = currentSection.querySelectorAll('h2');
            const currentSectionHeader = 
                                            (currentSectionH2.length ? currentSectionH2[0] :
                                                null);
            
            if (currentSectionHeader && menuInnerText == currentSectionHeader.innerText) {
                // Select menu
                menu.classList.add('active');
            } else {
                // Clear menu
                menu.classList.remove('active');
            }
        });
    } else {
        // Deselect all menus
        menus.forEach(menu => {
            menu.classList.remove('active');
        });
    }
}

window.addEventListener('scroll', updateMenu);
updateMenu(); // initial call

I was playing with this condition but it didn’t work,

        // Find current section; if not last in the list, stop immediately when found
        if(rect.top >= 0) {
            currentSection = sections[i];
            if (i < sections.length - 1) {
                break;
            }
        }

why is nested Data is getting lost after storing it in variable or if use it?

I am having this weird issue, i don’t know how.

let queryString = generateBlogApiQueryString(search, filters, page);

const response = await getApi(`blogs?${queryString}`);
const newBlogs = response?.data || [];

I have this code in nextjs 15. I am using Strapi v5 CMS as Admin/API.

I am fetching blogs with categories as relation. but when console the blogs as

console.log(response?.data)

then i am getting all blogs with categories.

but if I

console.log(newBlogs)

then i am getting the blogs but no categories in it.

There is one more observation i see.

const response = await getApi(`blogs?${queryString}`);
const newBlogs = response?.data || [];
console.log(newBlogs);

if (search === "" && (!filters.categories || filters.categories.length <= 0) && !filters?.dateFilter && page <= 1)
{
    let newFirstBlog = anything?.[0] || null;
    let newRightSideBlog = anything?.slice(1, 4) || [];
    let newRestBlogs = anything?.slice(4) || [];

    setFirstBlog(newFirstBlog);
    setRightSideBlogs(newRightSideBlog);
    setRestBlogs(newRestBlogs);
}
else
{
    setFirstBlog(null);
    setRightSideBlogs([]);
    setRestBlogs(newBlogs);
}

here if i am using anything(any variable useless) instead newBlogs then console shows blogs with categories but if i use the newBlogs variable

const response = await getApi(`blogs?${queryString}`);
const newBlogs = response?.data || [];
console.log(newBlogs);

if (search === "" && (!filters.categories || filters.categories.length <= 0) && !filters?.dateFilter && page <= 1)
{
    let newFirstBlog = newBlogs?.[0] || null;
    let newRightSideBlog = newBlogs?.slice(1, 4) || [];
    let newRestBlogs = newBlogs?.slice(4) || [];

    setFirstBlog(newFirstBlog);
    setRightSideBlogs(newRightSideBlog);
    setRestBlogs(newRestBlogs);
}
else
{
    setFirstBlog(null);
    setRightSideBlogs([]);
    setRestBlogs(newBlogs);
}

now categories again showing empty in blogs.

I have tried to clone data with nested array. first blog then categories one by one but again after assigning categories get empty.

API is working. all blogs with categories are showing in network tab in fetch request.

vitest fake timers and nested setTimeout

I have code in which setTimeout is called repeatedly. Essentially the callback of one setTimout sets another one and so forth. I’d like to test the delays with vi.useFakeTimers() so that the code is executed instantly but I can still verify the intended delays.

import { describe, it, afterEach, beforeEach, vi } from 'vitest'

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

async function countDown(timesLeft) {
    while (timesLeft > 0) {
        console.log("Counting down from " + timesLeft)
        await sleep(1000)
        timesLeft--
    }
}

describe('countDown', () => {
    beforeEach(() => {
        vi.useFakeTimers()
    })
    afterEach(() => {
        vi.useRealTimers()
    })
    it('works with runAllTimers', async () => {
        const promise = countDown(2)
        vi.runAllTimers()
        await promise
    })
})

Why doesn’t vi.runAllTimers() execute all the timeouts immediately? How can I fix this?

My actual use case is to test the retry behaviour of an axios client (using axios-retry) but I think I have boiled down my current blocker problem to this simple example.

What are your go-to tools and practices for clean, consistent code in React/Next.js projects? [closed]

I’m working on a React + Next.js project with TypeScript, and I want to make sure the code stays clean and consistent.

I’m already using Prettier for formatting and Husky for pre-commit hooks — but I’m interested in hearing about your go-to tools, plugins, or small habits that help with:

Code formatting (config tips?)
Linting rules or plugins you rely on?
Pre-commit setups (like lint-staged)
Type-checking and testing (tsc, Jest) — local or CI?
Any tips or examples from your projects would be helpful.

Update price/number based on multiple checked checkboxes

I’m setting up a package page with multiple packages/prices. To get an idea/example, similar to Spotify plans: https://www.spotify.com/nl/premium/#plans

The thing is that i want to update the price of the plan based on additional options, these options are checkboxes on the page. It can be none selected, one selected or multiple selected.

I managed to get so far to get it working for one checkboxes, but can’t get it working for multiple checkboxes if i just copy the code. Here’s my code:

Can someone help met to set it up for multiple checkboxes?

function calculate() {
  var j = document.getElementById("output");
  let span = document.getElementById("lala");
  var rege = /^[0-9]*$/;
  var tons = 12;
  if (rege.test(tons)) {
    val = parseInt(tons);
    var treesSaved = 10.45;
    if ($('input[name="mv-box"]').is(":checked")) {
      span.textContent = treesSaved + 4;
    } else {
      span.textContent = treesSaved;
    }
  } else
    alert("Error in input");
}
$(function() {
  $('input[name="mv-box"]').change(function() {
    calculate();
  });
});
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

<input type="checkbox" name="mv-box" value="1" aria-invalid="false"> MV-box + 4 euro

<input type="checkbox" name="airco" value="1" aria-invalid="false"> Airco + 6 euro

<br />

<span id="lala">10.45</span> euro

Trouble loading icon in SCSS using Angular 19

I’m facing an issue with loading an SVG icon inside a SCSS file in an Angular 19 project that’s deployed as a Salesforce Static Resource.

In my SCSS file, I’m using this:

content: url(/vx-grid-assets/icons/indeterminate-box.svg);

This works only during development, but when deployed to Salesforce, it doesn’t resolve the full path correctly. Instead, it tries to load:

http://saas-power-4087-dev-ed--c.scratch.container.force.com/vx-grid-assets/icons/indeterminate-box.svg

But because this is served as a static resource, it actually needs to be a relative path. So I need it to resolve like:

content: url(./vx-grid-assets/icons/indeterminate-box.svg);

However, if I use:

content: url(vx-grid-assets/icons/indeterminate-box.svg);
content: url(./vx-grid-assets/icons/indeterminate-box.svg);
content: url("vx-grid-assets/icons/indeterminate-box.svg");
content: url("./vx-grid-assets/icons/indeterminate-box.svg");

I get compilation errors from Angular.

My assets are configured in angular.json like this:

{
  "glob": "**/*",
  "input": "./common-libraries/vx-grid/vx-grid-resources/assets",
  "output": "vx-grid-assets"
}

So the assets are copied correctly and available at runtime under vx-grid-assets/, but I can’t reference them properly in SCSS without getting build errors.

Has anyone found a reliable way to make SCSS asset URLs work correctly in this setup?