How to fix Strapi 5 “middlewares” environment variables not being read when deploying to fly.io through GitHub Actions?

I have a basic Strapi 5 project. Inside I configured config/middlewares.ts like this:

export default ({ env }) => [
  {
    name: "strapi::cors",
    config: {
      origin: env("CORS_ORIGIN").split(","),
      methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"],
      headers: ["Content-Type", "Authorization", "Origin", "Accept"],
      keepHeaderOnError: true,
    },
  },
...

On fly.io I configured this environment variable along with
a bunch of others. I also have this GitHub Action:

name: Fly Deploy
on:
  push:
    branches:
      - main
jobs:
  deploy:
    name: Deploy app
    runs-on: ubuntu-latest
    concurrency: deploy-group    # optional: ensure only one action runs at a time
    steps:
      - uses: actions/checkout@v4
      - uses: superfly/flyctl-actions/setup-flyctl@master
      - run: flyctl deploy --remote-only
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

Which runs this Dockerfile on the fly.io builder:

FROM node:22-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
COPY . /app
WORKDIR /app

FROM base AS prod-deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile

FROM base AS build
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm run build

FROM base
COPY --from=prod-deps /app/node_modules /app/node_modules
COPY --from=build /app/dist /app/dist
EXPOSE 1337
CMD [ "pnpm", "start" ]

For some reason, every environment variable I am loading in a similair way in other config files are read properly, and even the one in my middleware is loaded locally. My GitHub Action reports an issue from the remote fly.io builder however:

Error: Could not load js config file /app/dist/config/middlewares.js:      ││   Cannot read properties of undefined (reading 'split')

Menu with submenus – The hardware back button takes to first submenu page clicked on instead of menu page

Just started learning React Native (using expo)
I have a profile.jsx in my app folder. It has multiple sub-menu. Each sub-menu also has a custom “<- Go Back” button which takes to profile.jsx.

File structure:

folder app has:

  1. index.jsx

  2. profile.jsx

  3. home.jsx

  4. (auth) folder

  5. (profileMenu) folder

  6. (category) folder

  7. product folder

(auth) has _layout, signIn, login, guest and register jsx files

(profileMenu) has _layout, Favourites, Wallet, Payment, Settings, etc. also all jsx files.

In profile.jsx I have clickable menu items of (profileMenu) which on click go to that particular jsx file.

The <- go back button inside the files themselves work as intended but when using android Hardware back button the page goes back to through all the profileMenu items I have clicked through instead of going back to the default page which is profile.jsx.

Then further once I am on profile.jsx and I click on another profileMenu option (other than the first clicked option) and use the HW back button it takes me to that first clicked file instead of profile.jsx.

I have tried both router.push() and router.replace(). Both show the same bug.

BUT I have a menu item this does not happen with- Logout. I click on it any number of items it does not show this bug. If I click on first, go back and click on another menu item, I am successfully able to go back to profile and the logout page does not appear.

My logout button takes me to another folder (auth) which contains a few other files like signIn.jsx guest.jsx. In all of these files the HW back button takes to the previous page like it should

I will attach the necessary snippets from all the relevant files and also attach an image of my file structure. Please do help me.

/app/ _layout.jsx:

import { Ionicons } from "@expo/vector-icons";

export default function Layout() {
  return (
    <Tabs
      screenOptions={{
        headerShown: false,
      }}
    >
      <Tabs.Screen
        name="profile"
        options={{
          title: "Profile",
        }}
      />

      <Tabs.Screen
        name="(auth)"
        options={{
          href: null,
        }}
      />
      <Tabs.Screen
        name="(profileMenu)"
        options={{
          href: null,
        }}
      />
    </Tabs>
  );
}

profile.jsx ->

//other imports etc.

const profile = () => {

  const router = useRouter();

  const handleMenuPress = (label) => {
  const menuItems = [
  
    { icon: 'percent', label: 'Promotions',  },
    { icon: 'settings',label: 'Settings', },
    { icon: 'log-out', label: 'Logout', },
  ];
    console.log(`Pressed: ${label}`);

    switch (label) {
      case 'Promotions':
        router.push('/(profileMenu)/Promotions');
        break;
      case 'Settings':
        router.push('/(profileMenu)/Settings');
        break;
      case 'Logout':
        router.replace('/(auth)/signIn');
        break;
      default:
        console.log('Unknown menu item');
    }
  }
  return (

    //other profile options line name etc...


      {/* Menu Items */}
      <ScrollView>
        <View>
          {menuItems.map((item, index) => {
            const IconComponent = item.iconSet;
            return (
              <TouchableOpacity
                key={index}
                onPress={() => handleMenuPress(item.label)}
              >
                <Text>{item.label}</Text>
              </TouchableOpacity>
            );
          })}
        </View>
      </ScrollView>
);
};


;
export default profile

Promotions.jsx:

//imports 
import { useRouter } from 'expo-router';

const Promotions = () => {
  const router = useRouter();

  const navigateToProfile = () => {
    router.replace('/profile');
  };
  return (
    <View>
      <View>
        <TouchableOpacity
          onPress={navigateToProfile}
        >
          <Text style={styles.backButtonText}>← Back</Text>
        </TouchableOpacity>
        <Text style={styles.title}>Payments</Text>
      </View>
      <Text>Promotions</Text>
    </View>
  )
}

export default Promotions

Settings.jsx:

//imports plus :
import { useRouter } from 'expo-router';


const Settings = () => {
  const router = useRouter();

  const navigateToProfile = () => {
    router.replace('/profile');
  };
  return (
    <View>
      <View>
        <TouchableOpacity
          onPress={navigateToProfile}
        >
          <Text>← Back</Text>
        </TouchableOpacity>
        <Text>Settings </Text>
      </View>
      <Text>
        Settings
      </Text>
    </View>
  )
}

export default Settings

in (profileMenu) folder: _layout.jsx:

import { Stack } from 'expo-router';

export default function ProfileLayout() {
  return (
    <Stack
      screenOptions={{
        headerShown: false,
      }}
    >
      <Stack.Screen name="Promotions" />
      <Stack.Screen name="Settings" />
      <Stack.Screen name="Wallet" />
    </Stack>
  );
}

(auth) _layout.jsx

export default function AuthLayout() {
  return (
    <Stack
      screenOptions={{
        headerShown: false,
      }}
    >
      <Stack.Screen name="signIn" />
      <Stack.Screen name="login" />
      <Stack.Screen name="register" />
      <Stack.Screen name="guest" />
    </Stack>
  );
}

signIn.jsx:

//imports
import { router } from 'expo-router';


const { width, height } = Dimensions.get('window');

const SignIn = () => {

    const menuItems = [
        { id: 1, label: 'login', text: 'Login to Your Account' },
        { id: 2, label: 'register', text: 'Create an Accound' },
        { id: 3, label: 'guest' },
    ]
    const handleMenuPress = (label) => {
        if (label === 'login') {
            router.push('/(auth)/login');
        } else if (label === 'register') {
            router.push('/(auth)/register');
        } else if (label === 'guest') {
            router.push('/(auth)/guest');
        }
    };

    return (
        <SafeAreaView>

            <ScrollView >
                <View >
                    <Text >Sign in to continue shopping</Text>
                </View>

                <View >
                    <View>

                        <TouchableOpacity
                            onPress={() => handleMenuPress(menuItems[0].label)} 
                        >
                            <Text>{menuItems[0].text}</Text>
                        </TouchableOpacity>

                        <TouchableOpacity
                            onPress={() => handleMenuPress(menuItems[1].label)}
                        >
                            <Text>{menuItems[1].text}</Text>
                        </TouchableOpacity>
                    </View>


                    <TouchableOpacity
                       
                        onPress={() => handleMenuPress('guest')}
                    >
                        <Text >Continue as Guest</Text>
                    </TouchableOpacity>
                </View>

            </ScrollView>
        </SafeAreaView>
    )
}

export default SignIn;

File Structure:

I have tried replacing the whole file twice, I have tried using router.push() as well as router.replace(). Both of them did not work. But for some reason logout menu item works.

I have replaced the _layout.jsx in both (auth) and (profileMenu) twice.
I am unsure at this point why my HardWare back Button is not functioning correctly.

How to get contentWindow of iframe without mounting to DOM

I’m working on a React app where I need to manage multiple iframes dynamically. Each iframe is created only once and stored in a cache (Map/Ref) for reuse. At any given time, only the selected iframe should be mounted in the DOM. Other iframes remain cached (not attached to the DOM).

My question is:
Is there a way to send messages/events (using postMessage or similar) to an iframe that is stored in cache but not currently mounted in the DOM? Currently i’m getting contentWindow value as null

Example workflow:

  1. Create iframe for itemId = 1, store it in cache.
  2. Switch to itemId = 2 → mount its iframe, keep itemId = 1 iframe in cache (not in DOM).
  3. From itemId = 2, I want to send a message/event to the iframe of itemId = 1.

Is this possible? Anyway?

DHTMLX Gantt – Internal server error when exporting

I’ve inherited a project that uses DHTMLX Gantt charts, and I was trying to understand why the export was not working as expected.

To achieve this, I recreated a small application to test the basics, however, here I am already running into an issue.

When trying to export a fairly simple project, with 3 sub-tasks, calling the export method gives me a beautiful black screen, stating “Internal server error. Error: Gantt to Excel. TypeError: Cannot read properties of undefined (reading ‘forEach’)

Here is the snippet:
https://snippet.dhtmlx.com/28horkya

Any help in understanding this is greatly appreciated.

Configuring ConsoleNinja to run in VSCode

I have installed ConsoleNinja extention in VSCode.

In powershell terminal, i run index.js file using node index.js I get the output of my index.js file but don’t see console.log message values I am supposed to see.

In the status bar I can see ConsoleNinja icon with the following message:

enter image description here

also I get the following in ConsoleNinja log file:

20:49:53.483 WARN host No clients connected

20:49:54.076 info buildHook-11924 allowed tools: vite, angular, jest, webpack, next.js, nest.js, cypress, http-server, serve, live-server, nuxt, remix, qwik, hydrogen, serverless, astro, live-server-extension, live-preview-extension, node

20:49:54.076 info buildHook-11924 running tools: live-preview-extension

20:49:54.083 info buildHook-11924 installing fs interceptor

20:49:54.082 info host client connected: buildHook { address: ‘127.0.0.1’, family: ‘IPv4’, port: 59380 }

20:49:54.131 info buildHook-11924 file processed (stat): 2 log points, error handler: false, network logging handler: false

20:49:54.140 info buildHook-11924 file processed (stat): 1 log points, error handler: false, network logging handler: false

20:49:54.158 info buildHook-11924 file processed (stat): 0 log points, error handler: false, network logging handler: false

20:49:54.198 info buildHook-11924 file processed (stat): 0 log points, error handler: false, network logging handler: false

20:49:54.231 info buildHook-11924 file processed (stat): 0 log points, error handler: false, network logging handler: false

20:49:54.246 info buildHook-11924 file processed (stat): 0 log points, error handler: false, network logging handler: false

20:49:54.250 info buildHook-11924 allowed tools: vite, angular, jest, webpack, next.js, nest.js, cypress, http-server, serve, live-server, nuxt, remix, qwik, hydrogen, serverless, astro, live-server-extension, live-preview-extension, node

20:49:54.250 info buildHook-11924 running tools: live-preview-extension

Also from the terminal running this command gives an error:

npx console-ninja run index.js

npx : File C:Program Filesnodejsnpx.ps1 cannot be loaded because running scripts is disabled on this system. For more information, see
At line:1 char:1

  • npx console-ninja run index.js

ConsoleNinja Settings in VSCode:

enter image description here

CSS/JS Animation not playing in some browsers

I’m using this javascript code to play two different kinds of animations (a “fade” and a “wipe”) when an element is visible. Everything works as intended in Firefox, but when I test it in Chrome/Edge, the “wipe” doesn’t work.

const options = {
  root: null, // null means you want to observe changes relative to the viewport
  threshold: 0.1, // Trigger when 10% of the element is visible
  rootMargin: '0px'
};

const callback = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      // Element is in view, add the animation
      entry.target.classList.add('animate');
    }
  });
};

const observer = new IntersectionObserver(callback, options);

const elements = document.querySelectorAll('.animate-fade, .animate-wipe');
elements.forEach(element => {
  observer.observe(element);
});
.animate-fade,
.animate-wipe {
  transition-timing-function: ease-out;
  transition-duration: 1.7s;
}

.animate-fade {
  opacity: 0;
}

.animate-wipe {
  clip-path: rect(0px 0% 100% 0px);
}

.animate {
  opacity: 1;
  clip-path: rect(0px 100% 100% 0px);
}
<div class="animate-fade">
  <p>This text has a fade effect and seems to work across browsers :).</p>
</div>
<div class="animate-wipe">
  <p>This text has a wipe effect and seems to only work on some browsers :(.</p>
</div>

How do I fix this?

How to preserve screen reader reading position when switching sections?

I’m building a React web application for blind users (screen reader users). The goal is to let users read a document while also being able to ask an AI chatbot questions at any time.

Intended flow:

  1. The user reads through the document with their screen reader.
  2. When they have a question, they switch to the chat section to ask it
    and listen to the answer.
  3. They return to the document and should ideally continue from the
    same spot they left off.

I’m unsure how to best preserve this reading position when switching between the two sections.

I’ve considered:

Option 1: Using clear headings, landmarks, or adding IDs for each paragraph so users can navigate back manually.

Question: Does this place too much cognitive demand on the user? What should I watch out for if I do it this way?

Option 2: On top of Option 1, also providing a “Return to where you left off” button or bookmark system.

Question: From my research, screen readers use a virtual cursor in “browse mode” so web developers cannot capture their screenreader cursor position directly, is such a feature actually doable? If so, how should I get their reading position?

Are there any other solutions?

In general, I would like to know:

Is this web design generally in line with accessibility guidelines?

What are the best practices in web development to help screen reader users resume reading from the same spot after switching to another interactive area?

why CSS mix-blend-mode: difference not working with Spline 3D background element

I’m trying to implement a mix-blend-mode: difference effect on header text that should blend with a 3D Spline background, but it’s not working. The navigation bar has the same blend mode and works perfectly, but the header text doesn’t show any color inversion.

this is my current setup:

  • Using Spline 3D background (<spline-viewer> element)

  • Navigation bar has mix-blend-mode: difference and works correctly

  • Header text has mix-blend-mode: difference but shows no effect

  • Both are positioned outside the main content to avoid stacking context issues

here the html code block:

<!-- 3D BACKGROUND -->
<spline-viewer id="spline-background" url="..."></spline-viewer>

<!-- NAVIGATION (working blend mode) -->
<div id="navigationbar">
    <span class="nav-name">Anubhab</span>
    <div class="nav-links">...</div>
</div>

<!-- HEADER TEXT (blend mode not working) -->
<div id="header">
    <h1>I am a Developer specializing in...</h1>
</div>

<!-- MAIN CONTENT -->
<main id="main-content">...</main>


here the css code block:


#spline-background { 
    position: fixed; 
    top: 0; 
    left: 0; 
    width: 100%; 
    height: 100%; 
    z-index: -2;
    pointer-events: none; 
}


#navigationbar { 
    position: fixed; 
    top: 0; 
    left: 0; 
    width: 100%; 
    z-index: 100; 
    mix-blend-mode: difference; /* This works */
}

#header {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 0;
}

#header h1 { 
    color: rgba(255, 255, 255, 0.75); 
    mix-blend-mode: difference; /* This doesn't work */
    z-index: -1;
}

full code here :
html, css, js

  1. Moved header outside main content to avoid opacity transitions

  2. Adjusted z-index values to ensure proper layering

  3. Removed isolation: isolate properties

  4. Added hardware acceleration (will-change: transform, transform: translateZ(0))

  5. Tried different blend modes (exclusion, multiply)

Header text should invert colors (white to black) when overlapping with dark parts of the 3D background, similar to how the navigation bar works.

Afterall:

Header text remains white without any blend effect, even when overlapping the 3D background elements.

Browser: Chrome (latest), Windows 11

Why is mix-blend-mode: difference working for the navigation bar but not for the header text, despite having similar positioning and CSS properties? Is there something specific about Spline 3D elements that prevents blend modes from working properly?

Inkscape SVG misaligned when put into React+Vite project and won’t rotate around hinge

I am making a microfuge tube that has two groups: the cap and the body. I used Inkscape to make the microfuge tube, and then I ran the XML file through Chat GPT to get a simplified version of the SVG file. However, when I import it into my program, the cap is not aligned with the body, and the cap doesn’t rotate around the hinge.Image of the microfuge tube in Inkscape. Image of the microfuge tube in React+Vite.

Attached below is the way I use it. Basically, I want the microfuge tube to switch between two states, one with the cap on and one with the cap off. But, I just can’t get the cap to align with the body, and it’s beyond me how to rotate it around the specific hinge.

export default function MicrofugeTube({ capOff = false }) {
  return (
    <svg
      viewBox="0 0 800 600"
      width={300}
      height={225}
      xmlns="http://www.w3.org/2000/svg"
    >
      {/* Animated Cap */}
      <motion.g
  transform="translate(110, -10000)" // adjust until hinge sits on neck
  style={{ transformOrigin: "85px 152px", transformBox: "fill-box" }}
  animate={{ rotate: capOff ? -120 : 0 }}
  transition={{ duration: 0.8, ease: "easeInOut" }}
>
  <path
    d="M84.8,151.7c0,3.06667,0,6.13333,0,9.2
      c22.17767-0.0983,44.38367,0.0623,66.54365-0.1976
      c3.18545-0.7008,6.3709-1.4016,9.55635-2.1024
      c0.71378-3.61798-1.47148-4.71625-4.73651-4.87603
      c-4.4144-1.35434-8.98911-1.38782-13.56349-1.42397
      c-0.0333-6.13333-0.0667-12.26667-0.1-18.4
      c-13.2-0.0667-26.4-0.13333-39.6-0.2
      c0,6,0,12,0,18c-6.033333,0-12.066667,0-18.1,0z"
    fill="#5b88b4"
    fillOpacity={0.5}
  />
</motion.g>
      {/* Tube Body */}
      <path
        d="M196.2,134.1v0.2z
          M196.2,134.1c-9.27074,0.38254-18.61158,0.25031-27.83401,1.07853
          c-2.3981,0.82584-6.14016,0.64247-7.73115,2.07439
          c-0.89721,3.74244,2.19367,4.14387,5.10309,4.69733
          c2.54792,0.59357,5.05574,1.31273,7.46207,2.34975
          c0.21053,26.36438,0.11861,52.74924,0.51779,79.10085
          c2.89407,22.13305,5.78814,44.2661,8.68221,66.39915
          c2.3272,2.15245,3.58971,6.21119,7.29718,5.89232
          c5.2662,0.78805,10.66732,0.53642,15.90282-0.39232
          c2.15558-2.83047,5.44348-5.16439,5.07117-9.10341
          c2.73994-21.5376,5.78218-43.05474,8.33348-64.6051
          c0.13178-25.76383,0.26357-51.52766,0.39535-77.29149
          c3.6118-0.24173,3.81689-2.82813,3.4-5.84261
          c1.24809-4.02448-1.97453-3.72759-4.99398-3.62709
          C210.60403,134.58642,203.40218,134.33825,196.2,134.1z"
        fill="#5b88b4"
        fillOpacity={0.5}
      />
    </svg>
  );
}

I’ve tried to change the translate(110,-10000), but there is no noticeable effect even when I go to extremes. I’ve also tried to change the viewBox, but that doesn’t do anything except make it smaller or larger.

How can I have intellisense in VS Code for opencv.js

I want to have intellisense for opencv.js inside VS Code when I call it from a JavaScript file, but when I use import "path/opencv.js" or import cv from "path/opencv.js", I can’t have intellisense for cv.

In the OpenCV documentation, there are following description and a example:

Once opencv.js is ready, you can access OpenCV objects and functions through cv object. The promise-typed cv object should be unwrap with await operator. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await .

imgElement.onload = await function() {
  cv = (cv instanceof Promise) ? await cv : cv;
  let mat = cv.imread(imgElement);`
}

but the intellisense doesn’t even show a promise-typed cv.

It is worth mentioning that my environment is browser.

`window.scrollTo` scrolls outside the container on iOS Safari

I’m running into an issue with window.scrollTo on iOS Safari.

On desktop browsers (Chrome/Firefox/Edge), this works as expected:

window.scrollTo(0, document.body.scrollHeight);

Even when resizing the window, the scroll goes correctly to the bottom of the page.

But on iOS Safari, the behavior is different:

  • It scrolls past the bottom of my main container (section#section).
  • The end of the <section> gets aligned at the top of the viewport.
  • I then need to manually scroll a little bit up/down before the browser “fixes” the scroll position (as if Safari realizes it scrolled outside the valid range).

Here’s the simplified HTML/CSS:

<html>
<head>
<style type="text/css">
body {
    height: 100vh;
}
section#section {
    height: auto;
}
</style>
</head>
<body>
<section id="section">
    [...]
</section>
</body>
</html>

Things I’ve already tried:

  • window.scrollTo(0, document.body.scrollHeight);

  • Scrolling the section itself:

    section.scrollTo({
        top: section.scrollHeight,
        behavior: 'smooth',
    });
    
  • Wrapping the scroll in a setTimeout (50ms delay).

Unfortunately, none of these fix the issue on iOS Safari.

Has anyone encountered this behavior before, or knows a reliable workaround to make sure the scroll goes correctly to the bottom on mobile Safari?

Thanks!

Kerberos: Getting username from service ticket using keytab file in JavaScript

I have a service ticket which I extracted from a Microsoft Active Directory joined computer. This service ticket then gets send to my server which has the keytab file generated for a custom SPN which was created for my service.

I want to extract the username from this service ticket using the generated keytab file. However, nothing seems to work. It is definitely possible, but the library support is just horrible and nearly all code examples I have stumbled across don’t work.

Is there some certified way of doing this? I don’t care if it is a CLI-Tool or a NpmJS package – I just need something.

Packages like kerb5 don’t seem to offer this feature and the kerberos library is somewhat broken for me.

If anyone has a certified way I would be very thankful for that code sample.

Sinon Snub Replace Function

Is it possible to replace or stub a function of the exports.

myStuff.js

function myOtherFunction() {
  return true;
}
function myFunction() {
  return myOtherFunction();
}

exports = {
  myOtherFunction: myOtherFunction,
  myFunction: myFunction
}

index.test.js:

const sinon = require('sinon');
const assert = require('assert');

describe('mystuff', function() {

  before{
    var myStuff = proxyquire('path/to/mystuff), {
      ...
    }
  }

  describe('my test', function() {
    beforeEach(function() {
      sinon.stub(myStuff, 'myOtherFunction');
    });
    it('should do something...', function() {
      myStuff.myOtherFunction.returns(false);
      assert.equal(myStuff.myOtherFunction(), false);
      assert.equal(myStuff.myFunction(), false);
    });
  });

});

When I I call the stubbed function ‘myOtherFunction’ directly it returns the stubbed value false, but when I call ‘myFunction’ which calls ‘myOtherFunction’ the original exported function is called.

I believe this is to do with references of the exported function, is there a way that I can over-ride the return function of ‘myOtherFunction’ when it is called by ‘myFunction’. I thought I might be able to manipulate it directly:

myStuff.myOtherFunction = function() { return false; }

Any ideas?

How does Node.js manage package files and directories? [closed]

I am using Node.js v24.1.0 to execute a JavaScript file in Terminal on macOS Sonoma 14.7.7.

node javascript/FizzBuzz.js

I noticed that Node.js created package files and directories, and I am curious about how they are managed.

Package files:

package-lock.json
package.json

Package directories:

node_modules/ansi-regex
node_modules/prompt-sync
node_modules/strip-ansi

Based on the timestamps, I observed that package-lock.json and package.json do not update every time that I execute my JavaScript file with node.

Will Node.js update the package files and directories if I use a new package in my JavaScript file and then execute it?

CSS 3D isometric cube – faces disappear at certain rotations

I’m trying to build a simple isometric cube with pure HTML and CSS (no WebGL).

At some rotation/frame, some faces of the cube are not shown. They are in the DOM, and they don’t have any of these properties (visibillity: hidden or dislplay: none).

I think this is related to perspective: none;. But I need it isometric.

Is there a way to prevent faces from disappearing?

The slider is just for debugging. Normally it is an on scroll animation.

(() => {
  const onReady = (fn) => {
    const wait = () => (window.anime && document.readyState !== 'loading') ? fn() : setTimeout(wait, 40);
    wait();
  };

  onReady(() => {
    const stage = document.querySelector('.stage');
    const cube = stage.querySelector('#cube');
    const topFace = stage.querySelector('#top');
    const leftFace = stage.querySelector('#left');
    const rightFace = stage.querySelector('#right');
    const backFace = stage.querySelector('#back');
    const range = stage.querySelector('#progress');
    const out = stage.querySelector('#progressOut');

    if (!cube || !topFace || !leftFace || !rightFace || !backFace || !range) return;

    const getS = () => parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--s')) || 280;
    const S = getS();
    const LIFT_PX = Math.round(S * 0.7);
    const FOLD_DEG = 90;

    topFace.style.setProperty('--lift-top', '0px');
    leftFace.style.setProperty('--fold-left', '0deg');
    rightFace.style.setProperty('--fold-right', '0deg');
    backFace.style.setProperty('--fold-back', '0deg');

    const makeVarTL = (el, name, from, to, unit, duration = 1000, easing = 'linear') => {
      const holder = {
        v: from
      };
      return anime.timeline({
          autoplay: false,
          duration,
          easing
        })
        .add({
          targets: holder,
          v: to,
          update: () => el.style.setProperty(name, holder.v + unit)
        });
    };

    const tlRotate = anime.timeline({
        autoplay: false,
        duration: 1600,
        easing: 'linear'
      })
      .add({
        targets: cube,
        rotateX: [0, 360],
        rotateY: [0, 360]
      });

    const tlTopLift = makeVarTL(topFace, '--lift-top', 0, LIFT_PX, 'px', 900);
    const tlLeft = makeVarTL(leftFace, '--fold-left', 0, -FOLD_DEG, 'deg', 700);
    const tlRight = makeVarTL(rightFace, '--fold-right', 0, FOLD_DEG, 'deg', 700);
    const tlBack = makeVarTL(backFace, '--fold-back', 0, FOLD_DEG, 'deg', 700);

    const seg = (g, a, b) => {
      if (g <= a) return 0;
      if (g >= b) return 1;
      return (g - a) / (b - a);
    };

    const seekAll = (g) => {
      const p1 = seg(g, 0.00, 0.25);
      const p2 = seg(g, 0.25, 0.50);
      const p3 = seg(g, 0.50, 0.75);
      const p4 = seg(g, 0.75, 1.00);

      tlRotate.seek(p1 * tlRotate.duration);
      tlTopLift.seek(p2 * tlTopLift.duration);
      tlLeft.seek(p3 * tlLeft.duration);
      tlRight.seek(p3 * tlRight.duration);
      tlBack.seek(p4 * tlBack.duration);
    };

    const onInput = () => {
      const g = (parseFloat(range.value) || 0) / 100;
      out.value = Math.round(g * 100) + '%';
      seekAll(g);
    };
    range.addEventListener('input', onInput);
    onInput();
  });
})();
:root {
  --s: min(20vmin, 340px);
  --isoX: -35.264deg;
  --isoY: 45deg;
  --lift-top: 0px;
  --fold-left: 0deg;
  --fold-right: 0deg;
  --fold-back: 0deg;
  --glass-rgb: 47 107 255;
  --glass-blur: 8px;
  --glass-sat: 160%;
  --glass-bright: 1.05;
}

.stage {
  width: 100%;
  height: 70vh;
  display: block;
  padding: 12px;
  box-sizing: border-box;
}

.controls {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
  font: 14px system-ui, sans-serif
}

.controls input[type="range"] {
  width: 320px
}

.cube-viewport {
  position: relative;
  width: 100%;
  height: calc(100% - 42px);
  overflow: visible;
  perspective: none;
}

.cube-camera {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transform-style: preserve-3d;
  transform: rotateX(var(--isoX)) rotateY(var(--isoY));
}

.cube-world {
  position: relative;
  width: var(--s);
  height: var(--s);
  transform-style: preserve-3d;
}

.cube {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  will-change: transform;
}

.face {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--s);
  height: var(--s);
  transform-origin: center;
  transform-style: preserve-3d;
  backface-visibility: hidden;
  border-radius: 12px;
  overflow: hidden;
  background:
    linear-gradient(135deg, rgb(var(--glass-rgb) / 0.22), rgb(var(--glass-rgb) / 0.08)),
    radial-gradient(120% 120% at 0% 0%, rgb(255 255 255 / 0.28), transparent 60%);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat)) brightness(var(--glass-bright));
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.28), inset 0 0 0 1px rgb(255 255 255 / 0.06);
}

.face::after {
  content: "";
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.06);
  pointer-events: none;
}

/* Face-Positionen */
#right {
  transform: translate(-50%, -50%) rotateY(-90deg) translateZ(calc(var(--s) * -0.5)) rotateX(var(--fold-right));
  transform-origin: 50% 100% 0;
}

#left {
  transform: translate(-50%, -50%) rotateY(-90deg) translateZ(calc(var(--s) * 0.5)) rotateX(var(--fold-left));
  transform-origin: 50% 100% 0;
}

#top {
  transform: translate(-50%, -50%) rotateX(90deg) translateZ(calc(var(--s) * 0.5 + var(--lift-top)));
}

#bottom {
  transform: translate(-50%, -50%) rotateX(90deg) translateZ(calc(var(--s) * -0.5));
}

#back {
  transform: translate(-50%, -50%) rotateX(0deg) translateZ(calc(var(--s) * -0.5)) rotateX(var(--fold-back));
  transform-origin: 50% 100% 0;
}
<main class="stage">
  <div class="controls">
    <label for="progress">Progress</label>
    <input id="progress" type="range" min="0" max="100" value="0" step="0.1" />
    <output id="progressOut">0%</output>
  </div>

  <div class="cube-viewport">
    <div class="cube-camera">
      <div class="cube-world">
        <div class="cube" id="cube">
          <div id="top" class="face">
            <div class="face-inner"></div>
          </div>
          <div id="bottom" class="face">
            <div class="face-inner"></div>
          </div>
          <div id="left" class="face">
            <div class="face-inner"></div>
          </div>
          <div id="right" class="face">
            <div class="face-inner"></div>
          </div>
          <div id="back" class="face">
            <div class="face-inner"></div>
          </div>
        </div>
      </div>
    </div>
  </div>
</main>

<!-- anime.js -->
<script src="https://unpkg.com/[email protected]/lib/anime.min.js"></script>