# Useful CSS Stuff

**URL:** <https://forum.inductiveautomation.com/t/useful-css-stuff/95414>\
**Category:** Ignition\
**Tags:** perspective\
**Created:** [September 2, 2024, 1:58am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414 "2024-09-02T01:58:19Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [September 2, 2024, 1:58am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/1 "2024-09-02T01:58:19Z")

</div>

I thought it might be good to have a topic with useful CSS that people have found and used over their journey to assist others or give ideas about how to achieve things via pure CSS rather than alternative methods.

If you choose to add to this topic, please include a screenshot demonstrating the effect! Also please add to the contents below by copying the link to your post ( ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/9/595ae1ffc5982d1bda008a4a5c9020361762a1c5.png)), adding a dot-point to the relevant section, selecting your text and pasting the url onto it.

## Contents

### Components

#### General

- [Absolute positioning components in flex containers for coord-container-like composition](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/27)

#### IA-Tree Component

- [Styling Tree Component Items](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/47)
- [Allowing Tree Component Item Text to Wrap](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/48)

#### IA-Tab Container

- [Hiding Tabs in the Tab Container](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/19)

#### IA-Alarm Status Table

- [Hiding the selection checkboxes in the Alarm Status Table](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/39)
- [Alternative Styling - Use Pills for Priority and State columns](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/62)

**IA-Table**

- [Sticky column](https://forum.inductiveautomation.com/t/how-to-freeze-a-column-in-table/92736/3)

### Popups

- [Allow popup height to be auto-sized](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/11)
- [Allow popup width to be auto-sized](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/12)

### Font

- [Using `rem` font size units and changing fontsize based on screen size](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/34)
- [Setting `rem` size using `calc` and media queries](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/42)

### Colours

- [Using CSS Custom Properties to Build Composable Style Systems](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/2#p-359485-using-css-custom-properties-to-build-composable-style-systems-1)
- [Using the `color-mix` CSS function to modify existing colours](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/7)
- [Using `from` within colours to change existing colours](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/13)
- [HSL (human-readable) colour cheat sheet](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/63)

### Animations

- [Adding a pulsating shadow](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/6)

### SVG/Images

- [Add tiled/repeating SVGs to create rollers](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/9)

### Misc

- Adding a “New” indicator with pure CSS (see bottom of this post)
- [Adding a \* to the start of a component with CSS](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/46)
- [General Advice #1](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/3)
- [General Advice #2](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/4)

In this spirit, here are some that i've come across (more to be added when I get time):

- Add a "NEW" indicator to components  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/6/d6a301589e3f8f482814a169d0a38a7155875d9a.png)

```python
/*
NewIndicator:
Adds a "NEW" box to the top-right of a component to indicate that the component was recently added.
Add a Perspective Style "NewIndicator" to make it selectable
*/
.psc-NewIndicator {
	position: relative; /* this sets the positioning system for the ::after component, otherwise the NEW will be positioned relative to the view, not the component */
	/*box-shadow: 0px 0px 0px 2px #C2DD53;*/ /* add a border around the component itself */
}
.psc-NewIndicator::after {
	content: 'NEW';
	position: absolute;
	top: 2px;
	right: 2px;
	width: 30px;
	height: 16px;
	padding: 2px;
	border-radius: 3px;
	background-color: #0A65EC;
	color: #fff;
	font-size: 0.75rem;
}

```

\*\*I still haven't mastered em/rem units... perhaps these could replace these fixed px units. I couldn't get the padding to apply properly to give equal space around the text and background edges for all sides

- `gap` for _all_ flex containers (flex views, flex containers, and flex repeaters) to add spacing around components within a flex container. This works for wrapped components as well.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/a/1a12647e07c2d747b100ffd6d5b7d9d18536be7f.png)

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [September 2, 2024, 3:14am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/2 "2024-09-02T03:14:48Z")

</div>

### Using CSS Custom Properties to Build Composable Style Systems

* * *

I'm gonna try to distill this down into something approachable.

Let's say you want to style your buttons. You make a palette of colors, and use those colors to style your button:

![20240901_224432](https://us1.discourse-cdn.com/inductiveautomation/original/3X/6/9/69fe466abfc445847f8f99308b482327aceb879a.png)

```css
[data-component='ia.input.button'] {
    color: var(--palette-primary-50);
    background-color: var(--palette-primary-500);
    border-color: var(--palette-primary-600);
    border-width: 1px;
    border-style: solid;
    border-radius: 5px;
}

:root {
    --palette-primary-50: #edf5fd;
    --palette-primary-100: #e3effb;
    --palette-primary-200: #c7dff7;
    --palette-primary-300: #97c3f0;
    --palette-primary-400: #4393e4;
    --palette-primary-500: #0b6bcb;
    --palette-primary-600: #185ea5;
    --palette-primary-700: #12467b;
    --palette-primary-800: #0a2744;
    --palette-primary-900: #051423;

    --palette-neutral-50: #fbfcfe;
    --palette-neutral-100: #f0f4f8;
    --palette-neutral-200: #dde7ee;
    --palette-neutral-300: #cdd7e1;
    --palette-neutral-400: #9fa6ad;
    --palette-neutral-500: #636b74;
    --palette-neutral-600: #555e68;
    --palette-neutral-700: #32383e;
    --palette-neutral-800: #171a1c;
    --palette-neutral-900: #0b0d0e;

    --palette-danger-50: #fef6f6;
    --palette-danger-100: #fce4e4;
    --palette-danger-200: #f7c5c5;
    --palette-danger-300: #f09898;
    --palette-danger-400: #e47474;
    --palette-danger-500: #c41c1c;
    --palette-danger-600: #a51818;
    --palette-danger-700: #7d1212;
    --palette-danger-800: #430a0a;
    --palette-danger-900: #240505;
}

```

Cool! But now _all_ our buttons are blue, and we need more options. We have 3 color palettes, and it would be nice if we could use them all on our buttons.

Let's add a layer of indirection:

```css
.psc-Color\/primary {
    --palette-50: var(--palette-primary-50);
    --palette-100: var(--palette-primary-100);
    --palette-200: var(--palette-primary-200);
    --palette-300: var(--palette-primary-300);
    --palette-400: var(--palette-primary-400);
    --palette-500: var(--palette-primary-500);
    --palette-600: var(--palette-primary-600);
    --palette-700: var(--palette-primary-700);
    --palette-800: var(--palette-primary-800);
    --palette-900: var(--palette-primary-900);
}

.psc-Color\/neutral {
    --palette-50: var(--palette-neutral-50);
    --palette-100: var(--palette-neutral-100);
    --palette-200: var(--palette-neutral-200);
    --palette-300: var(--palette-neutral-300);
    --palette-400: var(--palette-neutral-400);
    --palette-500: var(--palette-neutral-500);
    --palette-600: var(--palette-neutral-600);
    --palette-700: var(--palette-neutral-700);
    --palette-800: var(--palette-neutral-800);
    --palette-900: var(--palette-neutral-900);
}

.psc-Color\/danger {
    --palette-50: var(--palette-danger-50);
    --palette-100: var(--palette-danger-100);
    --palette-200: var(--palette-danger-200);
    --palette-300: var(--palette-danger-300);
    --palette-400: var(--palette-danger-400);
    --palette-500: var(--palette-danger-500);
    --palette-600: var(--palette-danger-600);
    --palette-700: var(--palette-danger-700);
    --palette-800: var(--palette-danger-800);
    --palette-900: var(--palette-danger-900);
}

```

And use this computed/indirect palette on our button:

```css
[data-component='ia.input.button'] {
    color: var(--palette-50);
    background-color: var(--palette-500);
    border-color: var(--palette-600);
    border-width: 1px;
    border-style: solid;
    border-radius: 5px;
}

```

Now, by applying the style classes `Color\neutral`, `Color\primary`, and `Color\danger` we can get three different looks for our button.  
 ![20240901_230337](https://us1.discourse-cdn.com/inductiveautomation/original/3X/8/e/8ee946acc5ed9d6836870192aa4f208bac3ffb70.png)

This is a simple example, but it can be taken much further.

Our [pyro-mui-joy](https://github.com/mussonindustrial/pyro/tree/main/packages/pyro-mui-joy) theme takes this to the extreme, with 4 variants (`solid`, `outlined`, `soft`, and `neutral`), 5 palettes (`neutral`, `primary`, `success`, `info`, and `danger`), and three sizes (`sm`, `md`, and `lg`) that can all be applied to a component to affect its look and feel.

 ![20240901_230858](https://us1.discourse-cdn.com/inductiveautomation/original/3X/e/7/e795d004bdc36a21fe30b3e07703271892165d0f.png)

 ![20240901_230921](https://us1.discourse-cdn.com/inductiveautomation/original/3X/b/1/b1b67b58c82b8516e2c79fc88031057f4b96f2a5.png)

 ![20240901_230945](https://us1.discourse-cdn.com/inductiveautomation/original/3X/8/e/8ee57e7f522e5ecbdc8bb6719c42c32b80f80b98.png)

When you get this scale though, handwriting the CSS classes becomes sadistic and you will want to turn to automation to generate them for you (maybe a topic for another time).

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [September 2, 2024, 7:06am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/3 "2024-09-02T07:06:39Z")

</div>

The most useful thing I can contribute is rather basic, but fundamental to playing with css:

step 1: Learn how to select a component: [CSS selectors - CSS: Cascading Style Sheets | MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors)  
This doesn't mean memorize everything, it means learn how it works and how you can use a search engine to find the selectors you need.  
step 2: Use the dev tools to find the element you want to style, then apply step 1 to target it.

That's where the "advanced" (meaning beyond perspective classes) css journey starts.

---

<div class="post-metadata">

**Author:** ![victordcq](https://avatars.discourse-cdn.com/v4/letter/v/f6c823/32.png) [@victordcq](https://forum.inductiveautomation.com/u/victordcq)\
**Post date:** [September 2, 2024, 9:03am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/4 "2024-09-02T09:03:22Z")

</div>

The browser devtools are your friend.  
Its easy to find elements and from there you can craft selectors.  
You can edit css in the devtools too to see if everything is working before copying the working code to perspective.

Adding in perspective classes given through the designer requires you to add:  
"psc-" in front of the class name.  
And if the class is in a folder the path "/" should become `"\/"`  
example : `.psc-Folder\/NewStyle`

There are also some relativly new css selectors like :has() which are very powerful. Many sources who say its not possible in css, dont known about :has yet. [:has() - CSS: Cascading Style Sheets | MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/:has)

Also note not all css stuff works in the designer, but does show up correctly in browsers. That is because the designer is behind in chromium version. (And maybe the workstation app too i dont know)

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [September 2, 2024, 3:24pm UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/5 "2024-09-02T15:24:56Z")

</div>

> [@victordcq](#):
>
> Adding in perspective classes given through the designer requires you to add:  
> "psc-" in front of the class name.  
> And if the class is in a folder the path "/" should become `"\/"`  
> example : `.psc-Folder\/NewStyle`

I’ll plug our plugin for those who are generating CSS externally and find the perspective style class syntax error prone.

> **[pyro/packages/postcss-perspective-style-class at...](https://github.com/mussonindustrial/pyro/tree/deb24af3a0bb24ad8ca55ff70cff7927e7411176/packages/postcss-perspective-style-class)**
>
> Resources, tools, and assets for Ignition by Inductive Automation 🛠️ - mussonindustrial/pyro

With this plugin,

```css
[psc='Folder1/class'] {
    color: black;
}

[psc='Folder1/Folder2/class'] {
    color: black;
}

[psc='Folder1/class1'].otherClass1 .otherClass2 {
    color: black;
}

```

will be processed to:

```css
.psc-Folder1\/class {
    color: black;
}

.psc-Folder1\/Folder2\/class {
    color: black;
}

.psc-Folder1\/class1.otherClass1 .otherClass2 {
    color: black;
}

```

---

<div class="post-metadata">

**Author:** ![Steve\_Laubach](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/steve_laubach/32/60900_2.png) [@Steve\_Laubach](https://forum.inductiveautomation.com/u/Steve_Laubach)\
**Post date:** [September 2, 2024, 6:14pm UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/6 "2024-09-02T18:14:13Z")

</div>

Pulse shadow to draw attention to an element while the style is active.

```python
.psc-pulse-shadow{
	animation: pulse-shadow .5s infinite;
}
@keyframes pulse-shadow {
  0% {
    	box-shadow: 0px 0px 5px 0px rgba(255,255,255,0.2);
  }
  100% {
  	box-shadow: 0px 0px 20px 20px rgba(255,255,255,0.5);
  }
}

```

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [September 16, 2024, 12:58pm UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/7 "2024-09-16T12:58:20Z")

</div>

Your post kickstarted a rework of how I theme things, so thanks for that.

I'll suggest one thing though: using `color-mix` to generate palettes based on a key color value.

For example, your danger palette could be written like this, basing the key value on the built-in `--error`:

```cs
--palette-danger: var(--error);

--palette-error-0: color-mix(in srgb, var(--palette-error) 0%, white);
--palette-error-50: color-mix(in srgb, var(--palette-error) 10%, white);
--palette-error-100:	color-mix(in srgb, var(--palette-error) 20%, white);
--palette-error-150:	color-mix(in srgb, var(--palette-error) 30%, white);
--palette-error-200:	color-mix(in srgb, var(--palette-error) 40%, white);
--palette-error-250:	color-mix(in srgb, var(--palette-error) 50%, white);
--palette-error-300:	color-mix(in srgb, var(--palette-error) 60%, white);
--palette-error-350:	color-mix(in srgb, var(--palette-error) 70%, white);
--palette-error-400:	color-mix(in srgb, var(--palette-error) 80%, white);
--palette-error-450:	color-mix(in srgb, var(--palette-error) 90%, white);
--palette-error-500:	color-mix(in srgb, var(--palette-error) 100%, white);
--palette-error-550:	color-mix(in srgb, var(--palette-error) 90%, black);
--palette-error-600:	color-mix(in srgb, var(--palette-error) 80%, black);
--palette-error-650:	color-mix(in srgb, var(--palette-error) 70%, black);
--palette-error-700:	color-mix(in srgb, var(--palette-error) 60%, black);
--palette-error-750:	color-mix(in srgb, var(--palette-error) 50%, black);
--palette-error-800:	color-mix(in srgb, var(--palette-error) 40%, black);
--palette-error-850:	color-mix(in srgb, var(--palette-error) 30%, black);
--palette-error-900:	color-mix(in srgb, var(--palette-error) 20%, black);
--palette-error-950:	color-mix(in srgb, var(--palette-error) 10%, black);
--palette-error-1000:	color-mix(in srgb, var(--palette-error) 0%, black);

```

There are a few more nuances but don't mind those. Here's a comparison, the top one is using this palette, the bottom one is the danger palette you posted.

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/9/19545f2a760fd14c0ab5f58ce46c2de0dfcf4940.png)

There might be a better color space to use with `color-mix`, or maybe it can be parameterized better, but I still have to dive into this.  
Now, This may not be game changing for you, as you already have a full css setup and I believe, some processing to generate your css, but for someone who wants to start creating palettes, this can change the whole game, especially if you base your key colors on built-in colors, then the whole thing sticks to the theme without having to actually touch theme files.

edit:  
error, warning, primary, success and neutral, based respectively on `--error`, `--warning`, `--info`, `--success` and `--neutral-50`

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/c/7/c7ec02fa7b8cd1ac4d6af23e2efb56d3096a9939.png)

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [September 16, 2024, 2:31pm UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/8 "2024-09-16T14:31:17Z")

</div>

Excellent, I’ll absolutely be checking this out.

---

<div class="post-metadata">

**Author:** ![Steve\_Laubach](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/steve_laubach/32/60900_2.png) [@Steve\_Laubach](https://forum.inductiveautomation.com/u/Steve_Laubach)\
**Post date:** [September 17, 2024, 12:57am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/9 "2024-09-17T00:57:09Z")

</div>

CSS only Rollers. Tiny repeated SVG inline CSS.

```python
.psc-rollers-horizontal{
	background-image: url('data:image/svg+xml,<%3Fxml version="1.0" encoding="UTF-8"%3F><svg width="9.5201mm" height="45.916mm" version="1.1" viewBox="0 0 9.5201 45.916" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient id="linearGradient33" x1="330.51" x2="346.39" y1="19.993" y2="19.993" gradientTransform="scale(.40373 2.4769)" gradientUnits="userSpaceOnUse"><stop stop-color="%235a5a5a" offset="0"/><stop stop-color="%23fff" offset=".48578"/><stop stop-color="%235a5a5a" offset="1"/></linearGradient></defs><g transform="translate(-132.03 -46.269)"><rect x="135.8" y="48.147" width="1.6738" height="42.069" rx=".83739" ry=".50933"/><rect x="133.46" y="49.546" width="6.3606" height="39.279" rx=".83739" ry="2.1832" fill="url(%23linearGradient33)" stroke="%23505050" stroke-linecap="round" stroke-linejoin="round" stroke-width=".05"/></g><rect x="-1.4211e-14" width="9.5201" height="2.2318" rx="0" ry="0" fill="%23505050" stroke-width=".3206"/><rect x="-1.4211e-14" y="43.684" width="9.5201" height="2.2318" rx="0" ry="0" fill="%23505050" stroke-width=".3206"/></svg>');
	background-repeat: repeat-x;
	background-size: contain;
}

.psc-rollers-vertical{
	background-image: url('data:image/svg+xml,<%3Fxml version="1.0" encoding="UTF-8"%3F><svg width="45.916mm" height="9.5201mm" version="1.1" viewBox="0 0 45.916 9.5201" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient id="linearGradient33" x1="330.51" x2="346.39" y1="19.993" y2="19.993" gradientTransform="scale(.40373 2.4769)" gradientUnits="userSpaceOnUse"><stop stop-color="%235a5a5a" offset="0"/><stop stop-color="%23fff" offset=".48578"/><stop stop-color="%235a5a5a" offset="1"/></linearGradient></defs><g transform="rotate(90 112.11 -19.925)"><rect x="135.8" y="48.147" width="1.6738" height="42.069" rx=".83739" ry=".50933"/><rect x="133.46" y="49.546" width="6.3606" height="39.279" rx=".83739" ry="2.1832" fill="url(%23linearGradient33)" stroke="%23505050" stroke-linecap="round" stroke-linejoin="round" stroke-width=".05"/></g><rect transform="rotate(90)" y="-45.916" width="9.5201" height="2.2318" rx="0" ry="0" fill="%23505050" stroke-width=".3206"/><rect transform="rotate(90)" y="-2.2318" width="9.5201" height="2.2318" rx="0" ry="0" fill="%23505050" stroke-width=".3206"/></svg>');
	background-repeat: repeat-y;
	background-size: contain;
}

```

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [September 17, 2024, 6:32am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/10 "2024-09-17T06:32:25Z")

</div>

I'm also exploring the possibilities of the `color()` function, and the `rgb(from {color})` syntax.  
I'm not quite sure yet how I'll put them to use, but I'm sure there's potential.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 10, 2024, 6:52am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/11 "2024-10-10T06:52:11Z")

</div>

Adding this to the adv. stylesheet will allow popups to be resized dynamically. Without this, the size gets stuck when you're trying to reduce the size (I believe). Taken from [here](https://forum.inductiveautomation.com/t/perspective-popup-height-changes-when-dragged/96657/6).

```python
.ia_popup[id^="popup-"]{
    height: auto !important;
    max-height: 95vh; /* caps the height to 95% of the view height */
}

```

Also, setting the popup View's `props.defaultSize.height: auto` will auto-size the popup's height dynamically to its content which is quite useful.

_ **Important note:** _ This stops working if the popup is moved by the user, as the size is then fixed and applied directly to the popup’s DOM by IA 😕 Not sure if there’s a way around this without JS

> **JS fix via HTML injection… This will remove the fixed static width and heights applied by IA when the user clicks on the popup’s titlebar to drag it – experimental at best**
>
> Add this into a new file `body_bottom.txt` created under:
> 
> `<Ignition install dir>/data/modules/com.inductiveautomation.perspective/injectables` (note: in v8.3 you’ll need to create most of this folderpath manually)
> 
> ```python
> <script>
> function removeInlineDimensionsAndObserve(elements) {
> elements.forEach(el => {
> // Skip if already being observed (check for a custom property)
> if (el._dimensionObserverAttached) return;
> el._dimensionObserverAttached = true;
> 
> // Remove initial inline styles
> el.style.removeProperty('height');
> el.style.removeProperty('width');
> 
> // Create a MutationObserver to watch for future style changes
> const observer = new MutationObserver(mutations => {
> mutations.forEach(mutation => {
> if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
> if (el.style.height) {
> el.style.removeProperty('height');
> console.log(`Removed inline height from ${el.tagName} with classes ${el.className}`);
> }
> if (el.style.width) {
> el.style.removeProperty('width');
> console.log(`Removed inline width from ${el.tagName} with classes ${el.className}`);
> }
> }
> });
> });
> 
> observer.observe(el, { attributes: true, attributeFilter: ['style'] });
> });
> }
> 
> function initObserver() {
> // Initial observation of existing elements
> const initialElements = document.querySelectorAll('.popup.ia_popup');
> removeInlineDimensionsAndObserve(initialElements);
> 
> // Watch for new elements being added to the DOM
> const domObserver = new MutationObserver(mutations => {
> mutations.forEach(mutation => {
> mutation.addedNodes.forEach(node => {
> // Check if the added node is an element
> if (node.nodeType === 1) {
> // Check if the node itself matches
> if (node.matches && node.matches('.popup.ia_popup')) {
> removeInlineDimensionsAndObserve([node]);
> }
> // Check for matching descendants
> const descendants = node.querySelectorAll ? node.querySelectorAll('.popup.ia_popup') : [];
> if (descendants.length > 0) {
> removeInlineDimensionsAndObserve(descendants);
> }
> }
> });
> });
> });
> 
> // Start observing the entire document for added nodes
> domObserver.observe(document.body, {
> childList: true,
> subtree: true
> });
> 
> console.log(`Started observing DOM for .popup.ia_popup elements. Initial count: ${initialElements.length}`);
> }
> 
> // Wait for DOM to be ready
> if (document.readyState === 'loading') {
> document.addEventListener('DOMContentLoaded', initObserver);
> } else {
> // DOM is already ready
> initObserver();
> }
> </script>
> 
> ```

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 11, 2024, 11:16pm UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/12 "2024-10-11T23:16:27Z")

</div>

While on expandable popups, using the same technique as above for expanding the width of popups has odd results. So for this, I add the same css for the height, but instead of setting the popup view width to auto, I explicitly set the `props.defaultSize.width` value with a binding which dynamically changes the width of the popup in the client.

CSS:

```python
.ia_popup[id^="popup-"]{
    width: auto !important;
}

```

Edit: forgot to attach the demo:

There's a bit of a glitch where if the popup was set to open using auto positioning in the centre of the page and you haven't moved it, expanding it will continue to honour this. If you move it however, then the positioning becomes absolute and the popup will keep its top/left position when it's resized.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 14, 2024, 5:53am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/13 "2024-10-14T05:53:59Z")

</div>

You can create new colours from existing colours, for example changing the alpha channel of existing colours, explicity setting any of the h, s, l / r, g, or b values, or applying calculations to them:

```python
hsl(from var(--your-css-variable) h s l / 80%) # set the alpha channel to 80%
hsl(from var(--your-css-variable) h 30% l) # set the saturation of the colour to 30%
rgb(from aliceblue r g calc(b + 50)) # sets the blue value of the aliceblue colour to the blue value + 50 

```

Edit: I realise now that this is an elaboration of this!

> [@pascal.fragnoud](#):
>
> and the `rgb(from {color})` syntax.

---

<div class="post-metadata">

**Author:** ![louagejasper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/louagejasper/32/86311_2.png) [@louagejasper](https://forum.inductiveautomation.com/u/louagejasper)\
**Post date:** [October 15, 2024, 10:22am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/14 "2024-10-15T10:22:05Z")

</div>

> [@nminchin](#):
>
> ```python
> .ia_popup[id^="popup-"]{
> height: auto !important;
> }
> 
> ```

This is golden! How did I not know this!

> [@nminchin](#):
>
> I explicitly set the `props.defaultSize.width` value with a binding which dynamically changes the width of the popup in the client.

To what to do you bind the defaultSize.width?

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 15, 2024, 10:37am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/15 "2024-10-15T10:37:21Z")

</div>

Usually when I want a popup to increase in width, it'll be because a user has pressed a button to expand it, so I would have view custom props called:

```python
normalWidth: 400
expandedWidth: 600
currentWidth: "normal"

```

and then bind the default width to currentWidth, selecting either normal or expanded

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 15, 2024, 10:45am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/16 "2024-10-15T10:45:01Z")

</div>

~~I feel every time I revisit font sizing in Perspective, i'm constantly changing my thoughts.. I've gone from `px` to `clamp(0px, calc(f*(1.0vmin + 1.0vmax)), 24px)` to `em` to `rem`...  
And now that I've cycled through the different units, I think I have to go back to `clamp(0px, calc(f*(1.0vmin + 1.0vmax)), 24px)` especially for coordinate containers set to `percent`, using this simple example:~~

~~Edit: I edited the above to add `clamp` to the `calc`, as you'll probably want to cap the size to not get too large (or small)~~

~~Edit2: `clamp(0px, calc(f*(1.0vmin + 1.0vmax)), 24px)` - `f` is a floating value, e.g. 0.8. Use this to control the font size. Use different values for your different standard font size variables.~~

[See this update](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/34)

---

<div class="post-metadata">

**Author:** ![louagejasper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/louagejasper/32/86311_2.png) [@louagejasper](https://forum.inductiveautomation.com/u/louagejasper)\
**Post date:** [November 15, 2024, 3:23pm UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/17 "2024-11-15T15:23:52Z")

</div>

> [@nminchin](#):
>
> clamp(0px, calc(f\*(1.0vmin + 1.0vmax)), 24px)

What is f in this function?

---

<div class="post-metadata">

**Author:** ![jlandwerlen](https://avatars.discourse-cdn.com/v4/letter/j/bc8723/32.png) [@jlandwerlen](https://forum.inductiveautomation.com/u/jlandwerlen)\
**Post date:** [November 15, 2024, 4:00pm UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/18 "2024-11-15T16:00:50Z")

</div>

`f` is just a placeholder for a value (float typically). Replace `f` with your value.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [January 27, 2025, 11:06pm UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/19 "2025-01-27T23:06:11Z")

</div>

You can hide tabs in the Tab Container using the `disabled` prop in the `props.tabs` along with the following settings in the `props.tabStyle.disabled` props:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/a/4/a4aca7e5d9c98559bb11ea93634144a4e6ca52b4.png)

Edited to use `display` instead of `position` and `visibility` courtesy of @YF129701

---

<div class="post-metadata">

**Author:** ![YF129701](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/yf129701/32/60498_2.png) [@YF129701](https://forum.inductiveautomation.com/u/YF129701)\
**Post date:** [January 28, 2025, 2:24am UTC](https://forum.inductiveautomation.com/t/useful-css-stuff/95414/20 "2025-01-28T02:24:59Z")

</div>

I know I could just try it out myself and find out but what does the `position: absolute` do, if you know off the top of your head?

[Next page](https://forum.inductiveautomation.com/t/useful-css-stuff/95414.md?page=2)
