I am trying to develop a google web app that uses a google sheet in backend.

I want to develop locally in vscode as the editor in google Apps Scripts is rather limited. I also want to use vue3.

To setup my devel env I followed the only tutorial I found on the web. This tutorial uses Parcel.

Every thing goes well except the fact that the css placed into the style tag of each view component is not taken into account.

I found a workaround using inline styles but this is rather fastidious and frustrating.

The console in the browser tells me that :

Refused to apply style from 'https://n-7y7vlmirjfpoibpjjmpw7rb4z2lf2rvl3foebnq-0lu-script.googleusercontent.com/index.7692ac92.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.

The css file is the one produced by the build script defined in my package.json that is as follows:

{
  "name": "newcompta",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "start": "nodemon --watch ./src -e html,js,vue  --exec \"npm run build\"",
    "build": "parcel build ./src/index.html --dist-dir ./appsscript",
    "glogin": "clasp login",
    "glogout": "clasp logout",
    "gpull": "clasp pull",
    "gpush": "clasp push",
    "gcreate": "clasp create --title \"NEWCOMPTA\" --rootDir ./appsscript",
    "gstart" :"clasp push --watch "
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@google/clasp": "^2.4.2",
    "@parcel/transformer-vue": "^2.10.2",
    "@types/google-apps-script": "^1.0.77",
    "parcel": "^2.10.2"
  },
  "dependencies": {
    "nodemon": "^3.0.1",
    "vue": "^3.3.8"
  }
}

I restarted a project from scratch in order to see if the problem exist from the beginning and it is the case.

Here are some file examples with a minimalist project.

index.html

  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module">
      import "./index.js";
    </script>
  </body>
</html>

App.vue

<template>
    <Tabs>
    <Tab active="true" title="First Tab">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce gravida purus vitae vulputate commodo.
    </Tab>
    <Tab title="Second Tab">
      Cras scelerisque, dolor vitae suscipit efficitur, risus orci sagittis velit, ac molestie nulla tortor id augue.
    </Tab>
    <Tab title="Third Tab">
      Morbi posuere, mauris eu vehicula tempor, nibh orci consectetur tortor, id eleifend dolor sapien ut augue.
    </Tab>
    <Tab title="Fourth Tab">
      Aenean varius dui eget ante finibus, sit amet finibus nisi facilisis. Nunc pellentesque, risus et pretium hendrerit.
    </Tab>
  </Tabs>
</template>

  <script setup>
  import {ref } from 'vue'
  import Tabs from './components/Tabs.vue'
    import Tab from './components/Tab.vue'
  const name =ref('Vue')

  </script>

index.js

import {createApp} from "vue";
import App from "./App.vue";
const app = createApp(App);
app.mount("#app");

**Tab.vue**
<script setup>
  import { ref, onMounted } from 'vue';
  const props = defineProps([ 'active' ]);
</script>

<template>
  <div class="tab" :class="(active == 'true') ? 'active' : ''" ref="tabs">
    <slot></slot>
  </div>
</template>

<style>
  .tab {
    display: none;
  }
  .tab.active {
    display: block;
  }
</style>

Tabs.vue

<script setup>
  import { ref, onMounted, reactive } from 'vue';
  const props = defineProps([ 'customClass' ]);
  let tabContainer = ref(null);
  let tabHeaders = ref(null);
  let tabs = ref(null);
  let activeTabIndex = ref(0);

  onMounted(() => {
    tabs.value = [ ...tabContainer.value.querySelectorAll('.tab') ];
        for(let x of tabs.value) {
        if(x.classList.contains('active')) {
                activeTabIndex = tabs.value.indexOf(x);
            }
    }
  })
  const changeTab = (index) => {
    activeTabIndex = index;
    for(let x of [...tabs.value, ...tabHeaders.value]) {
        x.classList.remove('active')
    }
        tabs.value[activeTabIndex].classList.add('active')  
        tabHeaders.value[activeTabIndex].classList.add('active')  
  }
</script>

<template>
  <div id="tabs-container" :class="customClass" ref="tabContainer">
    <div id="tab-headers">
      <ul>
        <!-- this shows all of the titles --> 
        <li v-for="(tab, index) in tabs" :key="index" :class="activeTabIndex == index ? 'active' : ''" @click="changeTab(index)" ref="tabHeaders">{{ tab.title }}</li>
      </ul>
    </div>
    <!-- this is where the tabs go, in this slot -->
    <div id="active-tab">
        <slot></slot>
    </div>
  </div>
</template>

<style>
  #tab-headers ul {
    margin: 0;
    padding: 0;
    display: flex;
    border-bottom: 2px solid #ddd;
  }
  #tab-headers ul li {
    list-style: none;
    padding: 1rem 1.25rem;
    position: relative;
    cursor: pointer;
  }
  #tab-headers ul li.active {
    color: #008438;
    font-weight: bold;
  }
  
  #tab-headers ul li.active:after {
    content: '';
    position: absolute;
    bottom: -2px;
    left: 0;
    height: 2px;
    width: 100%;
    background: #008438;
  }
  #active-tab, #tab-headers {
    width: 100%;
  }
  
  #active-tab {
    padding: 0.75rem;
  }
</style>

and this is what is rendered

enter image description here

1

There are 1 best solutions below

0
Meaulnes On

Watching this tutorial on youtube I eventually understood the solution. Instead of pushing directly the results of the build, you have to transform the .js and .css file of the build into .js.html and .css.html not only changing their extension but also including their content inside a script or style tag. The tutorial provides the gas.js that does this transformation.

Moreover, you should also replace index.html with that fixed content

<!DOCTYPE html>
<html>
  <head>
    <!-- <base target="_top"> -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>vuejs-gas-template</title>
    <?!= include('vue/app.css') ?>
  </head>
  <body>
    <div id="app"></div>
    <?!= include('vue/manifest.js') ?>
    <?!= include('vue/vendor.js') ?>
    <?!= include('vue/app.js') ?>
  </body>
</html>

Of course name of the files and folder depends on what you app uses. In my case, I no longer used the boiler plate made from parcel but an other one according to the cited tutorial above.