<style>
    custom-element {
        --text-color: purple;
    }
    div.text {
        color: red !important;
        text-decoration: underline;
        font-size: 36px;
    }
</style>

<template>
    <style>
        .text {
            color: var(--text-color, blue);
            text-decoration: overline;
            font-size: 28px;
        }
    </style>
    <div class="text">
        In the Shadow
    </div>
</template>

<script>
    const template = document.querySelector('template');
    class CustomElement extends HTMLElement {
        constructor() {
            super();
            this.attachShadow({mode: "open"});
            this.shadowRoot.appendChild(template.content.cloneNode(true));
        }

        changeStyles(styles) {
            const textElement = this.shadowRoot.querySelector('.text');
            Object.keys(styles).forEach(styleKey => {
                textElement.style[styleKey] = styles[styleKey];
            })
        }

        static get observedAttributes() {
            return ['color', 'text-decoration'];
        }

        attributeChangedCallback(attribute, oldValue, newValue) {
            this.changeStyles({[attribute]: newValue});
        }
    }
    window.customElements.define('custom-element', CustomElement);
</script>

<custom-element></custom-element>
<div class="text">Outside the shadow</div>
// Code goes here

/* Styles go here */