Blazor Webassembly - Handling viewport / device-width changes - css

Recently I created a Custom Navmenu (using TailwindCSS) in my Blazor WebAssembly project.
Depending on the device-width a sidebar is being showed / hidden (with the use of Tailwind hidden and device-with CSS classes).
On a specific device-width a menu button is being showed, a div which is specific for the mobile sidebar is always hidden (with display: none), in Blazor I have created an #onclick function which sets the style of the mobile sidebar to empty (so display: none is being removed) and vice-versa.
This all works perfectly, however there is one problem with this approach. When the menu button is clicked, the mobile side-bar is being showed, if I now increase the device-width the menu button is being hidden automatically and so is the mobile sidebar, if i then decrease the device-width again the menu button is showed again but the 'opened' sidebar div is also being showed (because in the last smaller device-width is was also opened (style of display:none was removed).
The behavior should be, when the device width is increased the mobile-side bar should always retain its style property display:none.
Though, as far is I know there is no event in Blazor which triggers on a changed device width? is there any way to get this working?
Below some images and HTML code which shows the 'problem':
Normal size view:
Mobile view:
Mobile sidebar opened view:
So the problem is that when I go from Mobile sidebar opened view back to Normal size view and then back to Mobile view, it will show the Mobile sidebar opened view instead (because it still has an empty style property, which is equivalent to display: block)
The HTML / Blazor C# code :
<div class="md:hidden" style=#mobileSideBarDisplay> <!-- Blazor C# string that sets the display to display: none, or removes the display entirely to show it)
<div class="fixed inset-0 flex z-40">
<!--
Off-canvas menu overlay, show/hide based on off-canvas menu state.
Entering: "transition-opacity ease-linear duration-300"
From: "opacity-0"
To: "opacity-100"
Leaving: "transition-opacity ease-linear duration-300"
From: "opacity-100"
To: "opacity-0"
-->
<div class="fixed inset-0">
<div class="absolute inset-0 bg-gray-600 opacity-75"></div>
</div>
<!-- The menu item with is Blazor onclick event -->
<div class="relative flex-1 flex flex-col max-w-xs w-full pt-5 pb-4 bg-gray-800">
<div class="absolute top-0 right-0 -mr-14 p-1">
<button class="flex items-center justify-center h-12 w-12 rounded-full focus:outline-none focus:bg-gray-600" aria-label="Close sidebar" #onclick="ToggleSidebar">
<svg class="h-6 w-6 text-white" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<! -- rest of HTML, left out for readability -->
</div>
</div>
</div>
<!-- Static sidebar for desktop -->
<div class="hidden md:flex md:flex-shrink-0">
<div class="flex flex-col w-64">
<! -- rest of HTML, left out for readability -->
</div>
</div>
<!-- Horizontal navbar with the Menu button -->
<div class="flex flex-col w-0 flex-1 overflow-hidden">
<div class="relative z-10 flex-shrink-0 flex h-16 bg-white shadow">
<!-- menu button becomes visible at a certain device-width > done with the TailWind CSS class md:hidden -->
<button class="px-4 border-r border-gray-200 text-gray-500 focus:outline-none focus:bg-gray-100 focus:text-gray-600 md:hidden" aria-label="Open sidebar" #onclick="ToggleSidebar">
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h7" />
</svg>
</button>
<! -- rest of HTML, left out for readability -->
</div>
</div>
<!-- C# code for viewing mobile sidebar on Menu button press -->
#code {
private bool showMobileSideBar = false;
private string mobileSideBarDisplay => showMobileSideBar ? "" : "display: none;";
private void ToggleSidebar()
{
showMobileSideBar = !showMobileSideBar;
}
}

You need js interop and the window.resize event.
There are libraries that can help, like this one https://github.com/EdCharbeneau/BlazorSize

This answer is for anybody else who is here and interested in using a UI library.
In addition to #rdmpton's answer, MudBlazor has a component: <MudBreakpointProvider> which lets you get access to window resize values.
Check out the Getting Started Docs.
Use it like this:
<MudBreakpointProvider>
<MudHidden Breakpoint="Breakpoint.Xl" Invert="true">
<MudCard Class="pa-5">
<MudText>XL</MudText>
</MudCard>
</MudHidden>
<MudHidden Breakpoint="Breakpoint.Lg" Invert="true">
<MudCard Class="pa-5">
<MudText>LG</MudText>
</MudCard>
</MudHidden>
<MudHidden Breakpoint="Breakpoint.Md" Invert="true">
<MudCard Class="pa-5">
<MudText>MD</MudText>
</MudCard>
</MudHidden>
<MudHidden Breakpoint="Breakpoint.Sm" Invert="true">
<MudCard Class="pa-5">
<MudText>SM</MudText>
</MudCard>
</MudHidden>
<MudHidden Breakpoint="Breakpoint.Xs" Invert="true">
<MudCard Class="pa-5">
<MudText>XS</MudText>
</MudCard>
</MudHidden>
</MudBreakpointProvider>
Moreover, they have tons of other components to help you build nice UIs.

Related

Images Don't Render On Next 13.1.6

I try using flowbite carousel with next app. Before the page become interactive, the image has a hidden class. After page become interactive, a JavaScript file removes the hidden class and makes slider work. The problem is when JavaScript loads, and slider start sliding, the image become: (Rendered size: 0 × 0 px) and doesn't renders!
Here's The Carousel Code:
`import Image from "next/image";
export default function Slider() {
return (
<div id="default-carousel" className="relative" data-carousel="slide">
{/* Carousel wrapper */}
<div className="relative h-56 overflow-hidden rounded-lg md:h-96">
{/* Item 1 */}
<div className="hidden duration-700 ease-in-out" data-carousel-item>
<span className="absolute text-2xl font-semibold text-white -translate-x-1/2 - translate-y-1/2 top-1/2 left-1/2 sm:text-3xl dark:text-gray-800">
First Slide
</span>
<Image
src={"/assets/slide1.jpg"}
className="absolute block w-full -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 object-contain"
alt="..."
fill
/>
</div>
);
}
Removed unnecessary parts of code here.
Chrome Dev Tools:
Screenshot of error
I've tried fill property, giving static width and height. None of them worked.

how to find the name of the div that corresponds to the label used in the code inspector?

I am trying to find a way to add margin-auto to a tailwinds css class, so that I can center an svg image across a container. I can add the attribute in code inspect, but I cannot find the corresponding div tag in my code.
The inspect panel shows the div as tailwinds.css.1, but I don't have a class with that name. I have tried adding margin-auto to the svg tag itself and all the surrounding div tags, as well as the Logo component in which the svg is defined. None of them work to center the image.
How can I find the name of the div tag from the inspect panel?
<footer className="bg-slate-50">
<Container>
<div className="py-16 display-block mx-auto">
<Logo className="display-block mx-auto" />
mx-auto on the svg should do the trick
https://play.tailwindcss.com/o33Y2yn5KP
In order to inspect the required component you can use the DOM methods. For example if you want to target the <div className="py-16 display-block mx-auto"> , you can use like
document.getElementByTagName("Container").getFirstElementChild()
For centering the Logo, you just to need the mx-auto to that component only .I have rewritten your code below.
<script src="https://cdn.tailwindcss.com"></script>
<footer class="bg-slate-50">
<div class="container mx-auto"> <!-- adding the container class to the this outer div -->
<div class="py-16 display-block">
<!-- Consider it as you LOGO component -->
<svg class="h-6 w-6 flex-none fill-sky-100 stroke-sky-500 stroke-2 mx-auto" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="11" />
<path d="m8 13 2.165 2.165a1 1 0 0 0 1.521-.126L16 9" fill="none" />
</svg>
<!-- LOGO compnent ends -->
</div>
</div>
</footer>
Try to implement this but remember to replace class with className.
Also you can view the code with more better preview here
Did you tried to change display-block by only block on your logo component ?
display-block doesn't exist in tailwindcss

Cursor events with position fixed in css

so I've made this simple popup with tailwindcss that will show project description.
This is how it looks:
import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react";
import Button from "../../../components/elements/Button";
import ReactMarkdown from "react-markdown";
import { Repo } from "../../../lib/types/Repos";
interface PageProps {
setIsOpen: Dispatch<SetStateAction<boolean>>;
project: Repo;
}
export default function ProjectDescription({ setIsOpen, project }: PageProps) {
useEffect(() => {
document.body.style.overflow = "hidden";
}, []);
const close = () => {
document.body.style.overflow = "auto";
setIsOpen(false);
};
return (
<div
onClick={close}
className="fixed inset-0 h-full w-full flex z-20 justify-center items-center p-4 cursor-pointer"
style={{ backgroundColor: "rgba(0,0,0,0.3)" }}
>
<div className="bg-gray-50 shadow-md scrollbar z-[9999999] max-w-xl max-h-[600px] w-full h-auto px-8 py-8 overflow-auto rounded-md">
<div className="flex justify-between items-center">
<p className="text-gray-700">
Project built with:{" "}
<strong className="text-indigo-600">{project.language}</strong>
</p>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-8 w-8 text-gray-900 cursor-pointer"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
onClick={close}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</div>
{project.readme && (
<ReactMarkdown
children={project.readme}
className="prose prose-indigo"
/>
)}
<div className="mt-4">
<Button>Webiste</Button>
<a className="ml-4">Github</a>
</div>
</div>
</div>
);
}
Now the problem is that this container(upper div) has click event, and first div inside him also has same event??? Basically I can click on div inside and close modal. And that should not happen? Could someone please explain what's happening.
If you give onClick event to parent div you call same event by click the child dive. It is because the first thing you click is again will be the parent. Give z-9999 insted of z-[9999999] to the child div. Usually what you want to do when making a modal is set onClick(close) event to button not to entirly div itself. In your case you can set onHide(close) to parent div. With this way you close the modal by clicking outside of the modal.

w-screen and h-screen of tailwind are breaking the responsiveness

I am trying to make a 404 page with Nextjs and typescript. Here are the screenshots:
As you see, I have the scrollbar on both the right and bottom sides and they are breaking the responsiveness.
I gave the background color to make this noticeable.
Here's my code...
import Link from "next/link";
import { useRouter } from "next/router";
import React from "react";
interface Props {}
const ErrorPage: React.FC<Props> = ({}) => {
const router = useRouter();
return (
<>
<div className="bg-gray-900 h-screen w-screen flex justify-center items-center absolute z-0">
<svg
className="p-6 lg:p-48 fill-current text-gray-300"
viewBox="0 0 445 202"
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
// viewBox="0 0 24 24"
>
<path
d="M137.587 154.953h-22.102V197h-37.6v-42.047H.53v-33.557L72.36 2.803h43.125V124.9h22.102v30.053zM77.886 124.9V40.537L28.966 124.9h48.92zm116.707-23.718c0 22.46 1.842 39.643 5.525 51.547 3.684 11.905 11.23 17.857 22.64 17.857 11.411 0 18.89-5.952 22.44-17.857 3.548-11.904 5.323-29.086 5.323-51.547 0-23.54-1.775-40.97-5.324-52.29s-11.028-16.98-22.438-16.98c-11.41 0-18.957 5.66-22.64 16.98-3.684 11.32-5.526 28.75-5.526 52.29zM222.759.242c24.887 0 42.339 8.76 52.356 26.28 10.018 17.52 15.027 42.406 15.027 74.66s-5.01 57.095-15.027 74.525c-10.017 17.43-27.47 26.145-52.356 26.145-24.887 0-42.339-8.715-52.357-26.145-10.017-17.43-15.026-42.271-15.026-74.525 0-32.254 5.009-57.14 15.026-74.66C180.42 9.001 197.872.241 222.76.241zm221.824 154.711h-22.102V197h-37.6v-42.047h-77.355v-33.557l71.83-118.593h43.125V124.9h22.102v30.053zM384.882 124.9V40.537l-48.92 84.363h48.92z"
fillRule="nonzero"
/>
</svg>
</div>
<div className="h-screen w-screen flex justify-center items-center relative z-10">
<div className="p-6 text-center">
<h2 className="uppercase text-xl lg:text-5xl font-black">
We are sorry, Page not found!
</h2>
<p className="mt-3 uppercase text-sm lg:text-base font-semibold text-gray-900">
The page you are looking for might have been removed had its name
changed or is temporarily unavailable.
</p>
<div className="text-center">
<Link href="/">
<a
className="mt-6 m-auto bg-primary text-white py-4 px-6 w-1/3 block rounded-full tracking-wide
font-semibold font-display focus:outline-none focus:shadow-outline hover:bg-primaryAccent
shadow-lg transition-css"
>
Back To Homepage
</a>
</Link>
<button
onClick={() => router.back()}
className="mt-6 m-auto bg-primary text-white py-4 px-6 w-1/4 block rounded-full tracking-wide
font-semibold font-display focus:outline-none focus:shadow-outline hover:bg-primaryAccent
shadow-lg transition-css"
>
Go Back
</button>
</div>
</div>
</div>
</>
);
};
export default ErrorPage;
Is this happening because of h-screen and w-screen or is something wrong with flex? But if I don't use them, I don't make it to work as I am expecting which is placing one div above the other (absolute). I am probably looking at this code for too long and I am lost in space.
So, what am I doing wrong here? Thanks.
I would suggest to have a parent relative div
<div class="h-screen w-screen relative">
<!-- 404 -->
<div class="bg-gray-900 flex justify-center items-center absolute inset-0 z-0"> // inset-0 is important
<svg></svg>
</div>
<!-- content -->
<div class="flex justify-center items-center absolute inset-0 z-10">
</div>
</div>
I replicated your design.
I would recommend changing h-screen to min-h-screen.
You can check out an example using your code in the following link.
https://play.tailwindcss.com/eD7VEDN5AR
"width: 100vw" is breaking the page layout. There's a horizontal scrollbar appearing on the page.
That's because the w-screen property means "width: 100vw" (the element with such a property is supposed to consume the whole available device screen space horizontally).
As you have a vertical scrollbar on the page body () (as the body is probably bigger in height than the screen height (= 100vh)), you'll always have less gorizontal space than 100vw, as the horizontal space is also consumed by the vertical scrollbar.
It's not a TailwindCSS-related issue, but rather a CSS-related bug.
The vertical scrollbar's width is usually ~12 px, but it differs between OS'es and screen sizes.
What I think would be better is if CSS would count the available screen width for the 100vw property considering the scrollbar width, like 100vw = real screen width - (vertical scrollbar's width, if one is present).
"(height: 100vh // max-height: 100vh)" is breaking the page layout. There's a vertical scrollbar appearing
That's because the w-screen property means "width: 100vw" (the element with such a property is supposed to consume the whole available device screen space horizontally).
As you have a vertical scrollbar on the page body () (as the body is probably bigger in width than the screen width (= 100vw)), you'll always have less vertical space than 100vh, as the vertical space is also consumed by the horizontal scrollbar.
The horizontal scrollbar's width is usually ~12 px, but it differs between OS'es and screen sizes.
For this case CSS could count the property for the 100vh value like 100vh = real screen height - (horizontal scrollbar's height, if one is present).
What can be done now:
You better use width: 100% and height: 100% properties instead of width: 100vw and height: 100vh
It's always risky to use the vh and vw values. Why would you need that? Semantically, the child should always be smaller than the parent.
This is the same ugly workaround as hiding the problems with overflowing with "overflow-x: hidden".
So your code should look like:
import Link from "next/link";
import { useRouter } from "next/router";
import React from "react";
interface Props {}
const ErrorPage: React.FC<Props> = ({}) => {
const router = useRouter();
return (
<>
<div className="bg-gray-900 h-full w-full flex justify-center items-center absolute z-0">
<svg
className="p-6 lg:p-48 fill-current text-gray-300"
viewBox="0 0 445 202"
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
// viewBox="0 0 24 24"
>
<path
d="M137.587 154.953h-22.102V197h-37.6v-42.047H.53v-33.557L72.36 2.803h43.125V124.9h22.102v30.053zM77.886 124.9V40.537L28.966 124.9h48.92zm116.707-23.718c0 22.46 1.842 39.643 5.525 51.547 3.684 11.905 11.23 17.857 22.64 17.857 11.411 0 18.89-5.952 22.44-17.857 3.548-11.904 5.323-29.086 5.323-51.547 0-23.54-1.775-40.97-5.324-52.29s-11.028-16.98-22.438-16.98c-11.41 0-18.957 5.66-22.64 16.98-3.684 11.32-5.526 28.75-5.526 52.29zM222.759.242c24.887 0 42.339 8.76 52.356 26.28 10.018 17.52 15.027 42.406 15.027 74.66s-5.01 57.095-15.027 74.525c-10.017 17.43-27.47 26.145-52.356 26.145-24.887 0-42.339-8.715-52.357-26.145-10.017-17.43-15.026-42.271-15.026-74.525 0-32.254 5.009-57.14 15.026-74.66C180.42 9.001 197.872.241 222.76.241zm221.824 154.711h-22.102V197h-37.6v-42.047h-77.355v-33.557l71.83-118.593h43.125V124.9h22.102v30.053zM384.882 124.9V40.537l-48.92 84.363h48.92z"
fillRule="nonzero"
/>
</svg>
</div>
<div className="h-full w-full flex justify-center items-center relative z-10">
<div className="p-6 text-center">
<h2 className="uppercase text-xl lg:text-5xl font-black">
We are sorry, Page not found!
</h2>
<p className="mt-3 uppercase text-sm lg:text-base font-semibold text-gray-900">
The page you are looking for might have been removed had its name
changed or is temporarily unavailable.
</p>
<div className="text-center">
<Link href="/">
<a
className="mt-6 m-auto bg-primary text-white py-4 px-6 w-1/3 block rounded-full tracking-wide
font-semibold font-display focus:outline-none focus:shadow-outline hover:bg-primaryAccent
shadow-lg transition-css"
>
Back To Homepage
</a>
</Link>
<button
onClick={() => router.back()}
className="mt-6 m-auto bg-primary text-white py-4 px-6 w-1/4 block rounded-full tracking-wide
font-semibold font-display focus:outline-none focus:shadow-outline hover:bg-primaryAccent
shadow-lg transition-css"
>
Go Back
</button>
</div>
</div>
</div>
</>
);
};
export default ErrorPage;
In the case of TailwindCSS, you should use h-full and w-full instead of h-screen and w-screen.
Also, in your specific case it's this line that was breaking everything, most likely:
<div className="h-screen w-screen flex justify-center items-center relative z-10">
As you already have this applied to the parent element:
<div className="bg-gray-900 h-screen w-screen flex justify-center items-center absolute z-0">
It's all wrong semantically in your code. This is ugly work-arounding.
You haven't provided the tailwind.config.js, so some of the colors in the demo are missing due to missing custom TailwidnCSS class names values.
But this is basically the same code with an exception of eplacing all -screen properties with -full and also removing the bottom padding of the svg, as on most of the screens, the svg has too much spacing and then consumes >100vh space of the screen and is not positioned at the center vertically, but rather has starneg white space at the bottom of the page.
https://dpd0jl.sse.codesandbox.io/

How to not overlap components using Tailwind CSS?

I created a component named Header with a simple css class:
<template>
<nav
class="flex fixed w-full items-center justify-between px-6 h-16 bg-white text-gray-700 border-b border-gray-200 z-10"
>
<!-- Etc... -->
</nav>
In Home component I registred Header but it is overlapping the home:
<template>
<div class="container">
<Header />
<div class="flex m-5">
<h3>Hello</h3>
</div>
</div>
</template>
<script>
export default {
name: 'Home',
components: {
Header: () => import('#/components/Header.vue')
}
}
</script>
The Hello is behind, even including block class in Home component is not worked. Anyone can helped?
There are may ways you could achieve this, but building on the code you already have, you could:
Add a top-0 class to your header. This will ensure that your header which is now positioned fixed will stick to the top of the viewport.
Add a top padding class equavliant to the height of your header (e.g. pt-16) to your container.
Here's a live demo for your reference.
overlapping component each others because of height of your component and in flex height taking automaticly so remove your height
remove css h-16

Resources