How to change scrollbar when using Tailwind (next.js/react) - css

I'm using Tailwind (react/next) and struggle to change the way my scrollbar looks.
It's a single page application and I have been trying to create custom CSS to apply to the first div in my index file, like this:
<div className="no-scroll"> <<<<<<<--------- Adding custom css here
<Head>
<title>Oscar Ekstrand</title>
<link rel="icon" href="/images/favicon.ico" />
</Head>
<main className="flex flex-col no-scroll">
<section ref={heroref}>
<Hero scrollToContacts={scrollToContacts} />
</section>
<section ref={offeringref}>
<Offering />
</section>
<section ref={processref}>
<WhatIDo />
</section>
<section ref={biographyref}>
<CvBar />
</section>
<section ref={skillsetref}>
<Skillset />
</section>
</main>
<section ref={contactsref}>
<Footer />
</section>
</div>
I can get custom CSS classes to work for things like buttons, both with a "plugin-approach" and having a global style sheet. (https://play.tailwindcss.com/zQftpiBCmf)
But I can't understand how to change the look of my scrollbar.
Anyone got an idea?

Tailwind CSS doesn't provide a built-in way to customise the scrollbar styling. However, you can use the various ::-webkit-scrollbar pseudo-elements to style it.
Tailwind playground link: https://play.tailwindcss.com/5samiwyr4v.
#layer utilities {
.scrollbar::-webkit-scrollbar {
width: 20px;
height: 20px;
}
.scrollbar::-webkit-scrollbar-track {
border-radius: 100vh;
background: #f7f4ed;
}
.scrollbar::-webkit-scrollbar-thumb {
background: #e0cbcb;
border-radius: 100vh;
border: 3px solid #f6f7ed;
}
.scrollbar::-webkit-scrollbar-thumb:hover {
background: #c0a0b9;
}
}

yarn add -D tailwind-scrollbar
or
npm install --save-dev tailwind-scrollbar
then
plugins: [
// ...
require('tailwind-scrollbar'),
],
sample
<div class="h-32 scrollbar scrollbar-thumb-gray-900 scrollbar-track-gray-100">
<div class="h-64"></div>
</div>
variants
variants: {
// ...
scrollbar: ['dark']
}
FOR SOME INSTANCE IF YOU WANT TO CHANGE THE WIDTH OF THE SCROLLBAR YOU CAN CUSTOME IN YOUR tailwind.css
#layer utilities {
.scrollbar-medium::-webkit-scrollbar {
width: 12px;
}
}
then
<div class="h-32 scrollbar scrollbar-thumb-gray-900 scrollbar-track-gray-100 scrollbar-medium">
<div class="h-64"></div>
</div>
THERE IS ONLY ONE STYLE FOR SCROLLBAR WHICH IS scrollbar-thin... so customize this way

I've managed to style the scrollbar with Tailwin plugin like this:
// tailwind.config.js
const plugin = require('tailwindcss/plugin');
module.exports = {
// ...
plugins: [
plugin(({ addBase, theme }) => {
addBase({
'.scrollbar': {
overflowY: 'auto',
scrollbarColor: `${theme('colors.blue.600')} ${theme('colors.blue.200')}`,
scrollbarWidth: 'thin',
},
'.scrollbar::-webkit-scrollbar': {
height: '2px',
width: '2px',
},
'.scrollbar::-webkit-scrollbar-thumb': {
backgroundColor: theme('colors.blue.600'),
},
'.scrollbar::-webkit-scrollbar-track-piece': {
backgroundColor: theme('colors.blue.200'),
},
});
}),
],
// ...
};
And use like this:
<div class="scrollbar">
<!-- content -->
</div>

/* For Firefox Browser */
.scrollbar {
scrollbar-width: thin;
scrollbar-color: #000 #fff;
}
/* For Chrome, EDGE, Opera, Others */
.scrollbar::-webkit-scrollbar {
width: 20px;
}
.scrollbar::-webkit-scrollbar-track {
background: #fff;
}
.scrollbar::-webkit-scrollbar-thumb {
background:#000;
}

TailwindCSS's doc changed scrollbar styles by scrollbar:!w-1.5 scrollbar:!h-1.5 scrollbar:bg-transparent scrollbar-track:!bg-slate-100 scrollbar-thumb:!rounded scrollbar-thumb:!bg-slate-300 scrollbar-track:!rounded, the plugin they use
/** #type {import('tailwindcss').Config} */
module.exports = {
mode: 'jit',
content: ['./src/**/*.{html,ts,tsx,js}'],
darkMode: 'media',
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [
// https://github.com/tailwindlabs/tailwindcss.com/blob/ceb07ba4d7694ef48e108e66598a20ae31cced19/tailwind.config.js#L280-L284
function ({ addVariant }) {
addVariant(
'supports-backdrop-blur',
'#supports (backdrop-filter: blur(0)) or (-webkit-backdrop-filter: blur(0))',
);
addVariant('supports-scrollbars', '#supports selector(::-webkit-scrollbar)');
addVariant('children', '& > *');
addVariant('scrollbar', '&::-webkit-scrollbar');
addVariant('scrollbar-track', '&::-webkit-scrollbar-track');
addVariant('scrollbar-thumb', '&::-webkit-scrollbar-thumb');
},
],
};

Addition to that comment : https://stackoverflow.com/a/69410151/18041352
You can add darktheme option just adding
dark:scrollbarkdark or what you want to name that.
<div className="... overflow-auto scrollbar dark:scrollbarkdark> ...
Then add this in your main css file like in that comment above.
.scrollbar::-webkit-scrollbar-track {
background: white;
}
.scrollbardark::-webkit-scrollbar-track {
background: black;
}
...

Extend Tailwind by adding your own base styles on top of Preflight, simply add them to your CSS within a #layer base directive:
//Inside styles.css
#tailwind base;
#tailwind components;
#tailwind utilities;
#layer base {
::-webkit-scrollbar-thumb{
#apply bg-transparent shadow-sm
}
::-webkit-scrollbar{
#apply w-3 bg-transparent
}
::-webkit-scrollbar-thumb{
#apply rounded-none bg-blue-400 /*color trackbar*/
}
}
Check out the documentation [here][1]
[1]: https://tailwindcss.com/docs/preflight

Related

Tailwind - How to customize padding such as px-10

In Tailwind, I want to customize px-10 to equal
padding-left: 10;
padding-right: 10px;
How do I do that?
Many thanks.
You can add custom padding values as shown in their documentation
// tailwind.config.js
module.exports = {
theme: {
spacing: {
sm: '10px',
}
}
}
and use it as
<div class="px-sm"> .. <div/>
OR
Use your custom values inside class by using JIT mode
<div className="pl-[10px] pr-[10px]"> .. </div>

Material-UI: Remove TimelineItem missingOppositeContent:before element

I'm using Material-UI and building a timeline. My code is as follows:
<Timeline align="right" className={classes.monthlyContainer}>
<TimelineItem >
<TimelineSeparator className={classes.timelineSeparator}>
<TimelineDot className={classes.timelineDot} />
<TimelineConnector className={classes.timelineConnector} />
</TimelineSeparator>
{(data.map(url =>
<TimelineContent className={classes.memsImageContainer}>
<img
className={classes.memsImage}
src={url}
alt="MEMs"
/>
</TimelineContent>
))}
</TimelineItem>
</Timeline>
When I render the webpage, the Material-UI timeline keeps creating a .MuiTimelineItem-missingOppositeContent:before element which is shifting the layout of my timeline to the left.
When I inspect the element, this is what I see:
<li class="MuiTimelineItem-root MuiTimelineItem-alignRightMuiTimelineItem-missingOppositeContent">
<div class="MuiTimelineSeparator-root makeStyles-timelineSeparator-4">
<span class="MuiTimelineDot-root makeStyles-timelineDot-5 MuiTimelineDot-defaultGrey">
</span>
<span class="MuiTimelineConnector-root makeStyles-timelineConnector-6">
</span>
</div>
</li>
When I inspect the styles, this is what I have:
.MuiTimelineItem-missingOppositeContent:before {
flex: 1;
content: "";
padding: 6px 16px;
padding-left: 0px;
padding-right: 0px;
I have recreated it in codesandbox here
How can I remove this element?
The definition of the default styles for the missingOppositeContent element is as follows:
/* Styles applied to the root element if no there isn't TimelineOppositeContent provided. */
missingOppositeContent: {
'&:before': {
content: '""',
flex: 1,
padding: '6px 16px',
},
},
You can override the default styles using the same structure. Overriding this in the theme would look like the following:
const Theme = createMuiTheme({
overrides: {
MuiTimelineItem: {
missingOppositeContent: {
"&:before": {
display: "none"
}
}
}
}
});
You can also do this on a case-by-case basis (in case you have other situations in your code where you want the missing-opposite-content styling) using withStyles:
const TimelineItem = withStyles({
missingOppositeContent: {
"&:before": {
display: "none"
}
}
})(MuiTimelineItem);
You won't believe that you just need to add the <TimelineOppositeContent> component and set display property as 'none'. And it will be solved.

Vue.js dynamic <style> with variables

Is it possible to add the dynamic variable in style?
I mean something like:
<style>
.class_name {
background-image({{project.background}});
}
#media all and (-webkit-min-device-pixel-ratio : 1.5),
all and (-o-min-device-pixel-ratio: 3/2),
all and (min--moz-device-pixel-ratio: 1.5),
all and (min-device-pixel-ratio: 1.5) {
.class_name {
background-image({{project.background_retina}});
}
}
</style>
I faced the same problem. I have been trying to use a background color value from a database. I find out a good solution to add a background color value on inline CSS which value I set from database.
<img :src="/Imagesource.jpg" alt="" :style="{'background-color':Your_Variable_Name}">
With Vue.js 3.2 you can do State-Driven Dynamic CSS like this:
<template>
<h1 id="script">Script</h1>
<h1 id="scriptSetup">Script setup</h1>
</template>
<script>
export default {
data() {
return {
colorFromScript: 'red'
}
}
}
</script>
<script setup>
const colorFromScriptSetup = 'green'
</script>
<style>
#script {
color: v-bind('colorFromScript')
}
#scriptSetup {
color: v-bind('colorFromScriptSetup')
}
</style>
See an implementation here
The best way to include dynamic styles is to use CSS variables. To avoid inline styles while gaining the benefit (or necessity—e.g., user-defined colors within a data payload) of dynamic styling, use a <style> tag inside of the <template> (so that values can be inserted by Vue). Use a :root pseudo-class to contain the variables so that they are accessible across the CSS scope of the application.
Note that some CSS values, like url() cannot be interpolated, so they need to be complete variables.
Example (Nuxt .vue with ES6/ES2015 syntax):
<template>
<div>
<style>
:root {
--accent-color: {{ accentColor }};
--hero-image: url('{{ heroImage }}');
}
</style>
<div class="punchy">
<h1>Pow.</h1>
</div>
</div>
</template>
<script>
export default {
data() { return {
accentColor: '#f00',
heroImage: 'https://vuejs.org/images/logo.png',
}},
}
</script>
<style>
.punchy {
background-image: var(--hero-image);
border: 4px solid var(--accent-color);
display: inline-block;
width: 250px; height: 250px;
}
h1 {
color: var(--accent-color);
}
</style>
Also created an alternate more involved runnable example on Codepen.
CSS <style> is static. I don't think you can do that... you might have to look for a different approach.
You can try using CSS variables. For example, (the code below is not tested)
<template>
<div class="class_name" :style="{'--bkgImage': 'url(' + project.background + ')', '--bkgImageMobile': 'url(' + project.backgroundRetina + ')'}">
</div>
</template>
<style>
.class_name{
background-image: var(--bkgImage);
}
#media all and (-webkit-min-device-pixel-ratio : 1.5),
all and (-o-min-device-pixel-ratio: 3/2),
all and (min--moz-device-pixel-ratio: 1.5),
all and (min-device-pixel-ratio: 1.5) {
.class_name {
background-image: var(--bkgImageMobile);
}
}
</style>
Note: Only the latest browsers support CSS variables.
If you still see any issues with the :style in the template then try this,
<div :style="'--bkgImage: url(' + project.background + '); --bkgImageMobile: url(' + project.backgroundRetina + ')'">
</div>
As you are using Vue.js, use Vue.js to change the background, instead of CSS:
var vm = new Vue({
el: '#vue-instance',
data: {
rows: [
{value: 'green'},
{value: 'red'},
{value: 'blue'},
],
item:""
},
methods:{
onTimeSlotClick: function(item){
console.log(item);
document.querySelector(".dynamic").style.background = item;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.16/vue.js"></script>
<div id="vue-instance">
<select class="form-control" v-model="item" v-on:change="onTimeSlotClick(item)">
<option value="">Select</option>
<option v-for="row in rows">
{{row.value}}
</option>
</select>
<div class='dynamic'>VALUE</div>
<br/><br/>
<div :style="{ background: item}">Another</div>
</div>
Yes, this is possible. Vue.js does not support style tags in templates, but you can get around this by using a component tag. Untested pseudocode:
In your template:
<component type="style" v-html="style"></component>
In your script:
props: {
color: String
}
computed: {
style() {
return `.myJSGeneratedStyle { color: ${this.color} }`;
}
}
There are lots of reasons why you shouldn't use this method. It's definitely hacky and :style="" is probably better most of the time, but for your problem with media queries I think this is a good solution.
Vue 3 State-Driven Dynamic CSS Variables
I know this is a bit late and is using Vue.js 2, but as of now in Vue.js 3 you can create state-driven CSS variables.
You can now use your SFC (Single File Component) state data inside your styles tags using v-bind().
You can read more about state-driven CSS variables here, or read the Vue.js 3 documentation here.
Here is a code example
Example
<template>
<div>
<input type="text" v-model="color" />
<div class="user-input-color">
{{ color }}
</div>
</div>
</template>
<script>
export default {
data: () => ({
color: 'white'
})
}
</script>
<style scoped>
.user-input-color {
background-color: v-bind(color)
}
</style>
Here is a link to the live example.
Links
JS Now Vue state-driven CSS variables
Vue.js 3 Docs
Live Example
You can use the component tag offered by Vue.js.
<template>
<component :is="`style`">
.cg {color: {{color}};}
</component>
<p class="cg">I am green</p> <br/>
<button #click="change">change</button>
</template>
<script>
export default {
data(){
return { color: 'green' }
},
methods: {
change() {this.color = 'red';}
}
}
</script>
I encountered the same problem and I figured out a hack which suits my needs (and maybe yours).
As <style> is contained in <head>, there is a way to make it work:
We generate the CSS content as a computed property based on the state of the page/component
computed: {
css() {
return `<style type="text/css">
.bg {
background: ${this.bg_color_string};
}</style>`
}
}
Now, we have our style as a string and the only challenge is to pass it to the browser.
I added this to my <head>
<style id="customStyle"></style>
Then I call the setInterval once the page is loaded.
mounted() {
setInterval(() => this.refreshHead(), 1000);
}
And I define the refreshHead as such:
methods: {
refreshHead() {
document.getElementById('customStyle').innerHTML = this.css
}
}
In simple terms, this is how you would do it in Vue.js and Nuxt.js:
<template>
<div>
<img :src="dynamicImageURL" alt="" :style="'background-color':backgroundColor"/>
</div>
</template>
<script>
export default{
data(){
return {
dynamicImageURL='myimage.png',
backgroundColor='red',
}
}
}
</script>
I needed to write completely dynamic styles, so I used approach beyond Vue system:
{
// Other properties.
watch: {
myProp: {
handler() {
this.styleElement.innerHTML = this.myProp.css;
},
deep: true,
},
},
mounted() {
this.styleElement = this.document.createElement('style');
this.styleElement.innerText = this.myProp.css;
this.document.head.append(this.styleElement);
},
unmounted() {
this.styleElement.remove();
},
}
Though it may have some performace issues with CSS big enough.
I liked #mickey-mullin reply, but not everything worked entirely. The url missed require, even though the information in his post helped me a lot in my case.
var(), url(), multiple ternary operators (my own case - you shouldn't need it), I was able to do so for background-image in such a way:
template
<div :style="[
case1 ? { '--iconUrl': `url(${require('../../../public/icon1.svg')})`} :
case2 ? { '--iconUrl': `url(${require('../../../public/icon2.svg')})`} :
{ '--iconUrl': `url(${require('../../../public/default.svg')})` },
]" class="myClass">
styles
div.myClass::before {
background-image: var(--iconUrl);
}
Note: I didn't have to declare iconUrl in my data() -> return.

VUE component ignoring CSS

I have the following VUE component:
<template>
<div>
<div class="bottom-footer">
{{msg}}
</div>
</div>
</template>
<script>
export default {
name: 'LayoutFooter',
data () {
return {
msg: 'my test'
}
},
mounted () {
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.bottom-footer {
height: 200px;
background-color: #A7BFE8;
}
</scoped>
VUE is completely ignoring my scoped CSS. When page is rendered its simply not applied. There are no console errors. Ive tried removing the scoped attribute and its still ignored. Any ideas why VUE is doing this?
<style scoped>
.bottom-footer {
height: 200px;
background-color: #A7BFE8;
}
</style>
you need to close style

highcharts-ng grows but doesn't shrink in flexbox

I'm trying to put a highchart in a flexbox container. The chart grows with the container just fine, but it doesn't shrink when the container shrinks.
My project uses angularJS and highcharts-ng, although I'm guessing the problem lies with flexbox itself. It's the same in Chrome and IE at least.
Here's a plunkr that illustrates the issue. If you open the embedded view you'll notice when you make the window wider, both charts reflow to fit. But when you make it narrower, the top chart (in a flexbox), remains unchanged.
I put in the reflow button to broadcast the reflow event for highcharts-ng, but it seems to have no effect.
I'm looking for a workaround or any solution that will still let me use flexbox.
Below is the code from Plunkr. It is based on another plunk by the author of highcharts-ng that solved a similar issue for bootstrap containers.
index.html
<!DOCTYPE html>
<html ng-app="highChartTest">
<head>
<link data-require="bootstrap#3.3.2" data-semver="3.3.2" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
<link rel="stylesheet" href="style.css" />
</head>
<body ng-controller="MyHighChart as c">
<div class="page-container">
<div class="side-panel">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li><button ng-click="onReflowButtonPressed()" class="autocompare">Reflow</button></li>
</ul>
</div>
<div class="main-panel">
<highchart id="highchart01" config="c.config" height="300"></highchart>
</div>
</div>
<highchart id="highchart02" config="c.config" height="300"></highchart>
<script data-require="jquery#2.1.3" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script data-require="angular.js#1.3.15" data-semver="1.3.15" src="https://code.angularjs.org/1.3.15/angular.js"></script>
<script data-require="ui-bootstrap-tpls-0.12.0.min.js#*" data-semver="0.12.0" src="https://rawgit.com/angular-ui/bootstrap/gh-pages/ui-bootstrap-tpls-0.12.0.min.js"></script>
<script data-require="highcharts#4.0.1" data-semver="4.0.1" src="//cdnjs.cloudflare.com/ajax/libs/highcharts/4.0.1/highcharts.js"></script>
<script data-require="highcharts-ng#0.0.9" data-semver="0.0.9" src="https://rawgit.com/pablojim/highcharts-ng/master/dist/highcharts-ng.min.js"></script>
<script src="app.js"></script>
</body>
</html>
style.css
.page-container {
margin: 20px;
border: 1px solid black;
display: flex;
}
.side-panel {
width: 200px;
flex-shrink: 0;
background-color: #ddd;
}
.main-panel {
margin: 10px;
padding: 20px;
flex-grow: 1;
border: 1px solid blue;
}
.highcharts-container {
width: 100%;
border: 1px solid red;
}
app.js
(function() {
angular.module('highChartTest', ['ui.bootstrap', 'highcharts-ng'])
.controller('MyHighChart', ['$scope', '$timeout', MyHighChart]);
function MyHighChart($scope, $timeout) {
$scope.onReflowButtonPressed = function(){
console.log("broadcasting reflow");
$scope.$broadcast('highchartsng.reflow');
}
var chartId = 'yieldColumns01';
this.widget = {
chartId: chartId,
cols: '12',
title: 'Reflow Test'
};
this.config = {
options: {
chart: { type: 'column' },
legend: { enabled: false},
},
title: { enabled: false, text: ''},
xAxis: { categories: ['A', 'B', 'C', 'D', 'E', ] },
yAxis: { max: 100},
series: [{
name: '2014',
data: [90.9, 66.1, 55.0, 53.2, 51.2]
}],
size: { height: '300' },
// function to trigger reflow in bootstrap containers
// see: http://jsfiddle.net/pgbc988d/ and https://github.com/pablojim/highcharts-ng/issues/211
func: function(chart) {
$timeout(function() {
chart.reflow();
// //The below is an event that will trigger all instances of charts to reflow
//$scope.$broadcast('highchartsng.reflow');
}, 0);
}
};
}
})();
You can either set a non-relative width or flex-basis on the .main-panel container to help Highcharts figure out the parent element's dimensions.
I've also removed the width: 100% on .highcharts-container since it wasn't doing anything.
The original Plunkr works on Safari 9.0.1, so this could be due to differences in how browsers implement the flexbox spec (e.g. related issue with width/height: 100%)

Resources