Skip to content

[Tailwind | Angular] Responsive breakpoints in typescript

Tailwind breakpoints in typescript with an easy angular component

Georg Höller
Georg Höller
3 min read
[Tailwind | Angular] Responsive breakpoints in typescript
Photo by Blake Connally / Unsplash

Recently I stumbled over a problem with my tailwind breakpoints. For my sidebar component, I need to know in typescript which tailwind breakpoint is active.
I did what I do best - searched for a solution in stackoverflow. I found the following answer - which is the source for my tw-breakpoints angular component.

How do I get tailwinds active breakpoint in JavaScript?
I am building tailwind with config file and including it in a react project. I would like to get the active breakpoint value in javascript/React. How can I achieve the same.? <div class=”...

Solution

If you just want to see the final code to get tailwind breakpoints in typescript, check out the repo:

gh-utils/src/lib/tailwind-helpers/tw-breakpoints at main · Georg632/gh-utils
Contribute to Georg632/gh-utils development by creating an account on GitHub.

I created a new standalone component with the name tw-breakpoints:

ng g c tw-breakpoints --skipTests --skipImport --standalone

The template is quite simple:

<div
  #twBreakpointContainer
  class="absolute w-full left-0 right-0 h-0 opacity-0"
>
  <div #sm class="absolute block md:hidden"></div>
  <div #md class="absolute hidden md:block lg:hidden"></div>
  <div #lg class="absolute hidden lg:block xl:hidden"></div>
  <div #xl class="absolute hidden xl:block 2xl:hidden"></div>
  <div #xxl class="absolute hidden 2xl:block"></div>
</div>

I'm using the container div element for the ResizeObserver and it is responsible for making the component invisible.
Add a child for each breakpoint which should be available in typescript. Only one child should be visible at the same time.

Let's have a look at the typescript code:

import {
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  EventEmitter,
  Output,
  ViewChild,
} from '@angular/core';
import { CommonModule } from '@angular/common';

export enum TwBreakpoints {
  sm,
  md,
  lg,
  xl,
  xxl,
}

@Component({
  selector: 'tw-breakpoints',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [CommonModule],
  templateUrl: './tw-breakpoints.component.html',
})
export class TwBreakpointsComponent {
  @ViewChild('twBreakpointContainer', { static: true }) container!: ElementRef;
  @ViewChild('sm', { static: true }) sm!: ElementRef;
  @ViewChild('md', { static: true }) md!: ElementRef;
  @ViewChild('lg', { static: true }) lg!: ElementRef;
  @ViewChild('xl', { static: true }) xl!: ElementRef;
  @ViewChild('xxl', { static: true }) xxl!: ElementRef;

  @Output('breakpointChange') breakpointChange: EventEmitter<TwBreakpoints> =
    new EventEmitter<TwBreakpoints>();

  resizeObserver!: ResizeObserver;

  constructor() {}

  ngOnInit(): void {
    this.onResize();

    this.resizeObserver = new ResizeObserver((entries) => {
      this.debounce(() => {
        if (entries?.length > 0) this.onResize();
      });
    });
    this.resizeObserver.observe(this.container.nativeElement);
  }

  onResize() {
    if (this.offsetParentSet(this.sm)) {
      console.log('sm');
      this.breakpointChange.emit(TwBreakpoints.sm);
    }
    if (this.offsetParentSet(this.md)) {
      console.log('md');
      this.breakpointChange.emit(TwBreakpoints.md);
    }
    if (this.offsetParentSet(this.lg)) {
      console.log('lg');
      this.breakpointChange.emit(TwBreakpoints.lg);
    }
    if (this.offsetParentSet(this.xl)) {
      console.log('xl');
      this.breakpointChange.emit(TwBreakpoints.xl);
    }
    if (this.offsetParentSet(this.xxl)) {
      console.log('2xl');
      this.breakpointChange.emit(TwBreakpoints.xxl);
    }
  }

  offsetParentSet(el: ElementRef): boolean {
    return el.nativeElement.offsetParent != null;
  }

  ngOnDestroy(): void {
    this.resizeObserver.disconnect();
  }

  debounceResizeTimeout: any = null;
  private debounce(fn: Function) {
    if (this.debounceResizeTimeout) {
      clearTimeout(this.debounceResizeTimeout);
    }
    this.debounceResizeTimeout = setTimeout(function () {
      fn();
    }, 150);
  }
}

The ResizeObserver observes the container element - when the size changes it triggers the onResize function.
Here starts the interessting part. If the offsetParent property is null, the tailwind breakpoint from this element is not met.
Just emit the output eventemitter with the correct breakpoint and your parent component can handle the windows size change.

If you find a better solution for this problem - give me a hint in the comments!

If you have further questions about this topic or need help with a problem in general, please write a comment or simply contact me at yesreply@georghoeller.dev :)
AngularTailwind

Comments


Related Posts

Members Public

[Angular] Routing Helper - a Typesafe Routing Attempt

It always bothered me that you have to define the routing paths as strings. If a route changes, you have to change all references manually. There must be a better way! Routing Constants The constants are for your specific app - here you can define a class which represents your

[Angular] Routing Helper - a Typesafe Routing Attempt
Members Public

[ASP.NET Core 6.0 | Angular] Hosting with Ionos

Update for my previous posts about hosting an angular frontend and ASP.NET Core 6 backend. Prerequirements: * active ionos windows hosting subscription * asp.net core 6 project * buildable angular project Ionos Setup Open up your favorite FTP client and connect it to your ionos webspace. Create two new folders -

[ASP.NET Core 6.0 | Angular] Hosting with Ionos
Members Public

Transition from Windows to Mac - Frontend Developer

My Windows journey started with Windows 98 I think, maybe Windows 95. Now it is the end of 2022 and I was in need for a new laptop. I have been searching for weeks for a suitable windows notebook, tested an Asus ROG X13 but sent it back because hardware

Transition from Windows to Mac - Frontend Developer