Tag: made

Embedded Analytics Made Simple With Cumul.io Integrations

Browse through SaaS communities on Twitter, LinkedIn, Reddit, Discord, you name it and you’ll see a common theme appear in many of them. That theme can go by many names: BI, analytics, insights and so on. It’s natural, we do business, collect data, we succeed or fail. We want to look into all of that, make some sense of the data we have and take action. This need has produced many projects and tools that make the lives of anyone who wants to look into the data just a bit easier. But, when humans have, humans want more. And in the world of BI and analytics, “more” often comes in the form of embedding, branding, customized styling and access and so on. Which ends up meaning more work for developers and more time to account for. So, naturally there has been a need for BI tools that will let you have it all.

Let’s make a list of challenges you may face as the builder and maintainer of these dashboards:

  1. You want to make the dashboards available to end users or viewers from within your own application or platform
  2. You want to be able to manage different dashboard collections (i.e. “integrations”)
  3. You want to be able to grant specific user rights to a collection of dashboards and datasets
  4. You want to make sure users have access to data only relevant to them

Cumul.io provides a tool we call Integrations which helps solve these challenges. In this article I’ll walk you through what integrations are, and how to set one up. The cool thing is that for most of the points above, there is minimal code required and for the most part can be set within the Cumul.io UI.

Some Background — Integrations

An Integration in Cumul.io is a structure that defines a collection of dashboards intended to be used together (e.g. in the same application). It is also what we use to embed dashboards into an application. In other words, to embed dashboards into an application, we give the application access to the integration that they belong to. You can associate dashboards to an integration and administrate what type of access rights the end users of the integration will have on these dashboards and the datasets they use. A dashboard may be a part of multiple integrations, but it may have different access rights on different integrations. When it comes to embedding, there are a number of SDKs available to make life simple regardless of what your stack looks like. 😊

Once you have a Cumul.io account and if you are an “owner” of an organization in Cumul.io, you will be able to manage and maintain all of your integrations via the Integrations tab. Let’s have a look at an example Cumul.io account. Below you can see the Dashboards that one Cumul.io user might have created:

Although these are all the dashboards this user may have created, it’s likely that not all dashboards are intended for the same end-users, or application for that matter. So, the owner of this Cumul.io account would create and maintain an Integration (or more!) 💪 Let’s have a look at what that might look like for them:

So, it looks like the owner of this Cumul.io account maintains two separate applications.

Now let’s see what the process of creating an integration and embedding its dashboards into an application would look like. The good news is, as mentioned before, a lot of the steps you will have to take can be done within the Cumul.io UI.

Disclaimer: For the purposes of this article, I’ll solely focus on the Integration part. So, I’ll be skipping everything to do with dashboard creation and design and we will be starting with a pre-made set of imaginary dashboards.

What we will be doing:

Creating an Integration

For simplicity, let’s only create one integration for now. Let’s imagine we have an analytics platform that we maintain for our company. There are three dashboards that we want to provide to our end-users: the Marketing Dashboard, the Sales Dashboard and the Leads Dashboard.

Let’s say that out of all the dashboards this account has created or has access to, for this particular project they want to use only the following:

New Integration

To create the integration, we go to the Integrations tab and select New Integration. The dialogue that pops up will already give you some idea of what your next steps will be:

Selecting Dashboards

Next up, you will be able to select which of your dashboards will be included in this integration. You will also be able to give the Integration a name, which here I’ve decided will appropriately be “Very Important Integration”:

Once you confirm your selection, you will have the option of defining a slug for each dashboard (highly recommended). These can later be used while embedding the dashboards into your application. You will later see that slugs make it easy to reference dashboards in your front-end code, and make it easier to replace dashboards if needed too (as you won’t need to worry about dashboard IDs in the front-end code).

Access Rights

You will then get to set the integration’s access rights for the datasets its dashboards use. Here we set this to “Can view.” For more info on access rights and what they entail, check out our associating datasets to integrations:

Filters and Parameters (and Multi-Tenant Access)

Side Note: To help with multi-tenant access — which would make sense in this imaginary set up — Cumul.io makes it possible to set parameters and filters on datasets that a dashboard uses. This means that each user that logs into your analytics platform would only see the data they personally have access to in the dashboards. You can imagine that in this scenario access would be based on which department the end user works for in the company. For more on how to set up multi-tenancy with Cumul.io, check out our article, “Multi-Tenancy on Cumul.io Dashboards with Auth0”. This can be done within the dashboard design process (which we are skipping), which makes it easier to visualize what the filters are doing. But here, we will be setting these filters in the Integration creation process.

Here, we set the filters the datasets might need to have. In this scenario, as we filter based on the users’ departments, we define a department parameter and filter based on that:

And voilà! Once you’re done with setting those, you have successfully created an integration. The next dialogue will give you instructions for what will be your next steps for embedding your integration:

Now you’ll be able to see this brand new Integration in your Integration tab. This is also where you will have quick access to the Integration ID, which will later be used for embedding the dashboards.

Good news! After your Integration is created, you can always edit it. You can remove or add dashboards, change the slugs of dashboards or access rights too. So you don’t have to worry about creating new integrations as your application changes and evolves. And as editing an integration is all within the UI, you won’t need to worry about having a developer set it all up again. Non-technical users can adapt these integrations on the go.

Embedding Dashboards

Let’s see where we want to get to. We want to provide the dashboards within a custom app. Simple, user logs into an app, the app has dashboards, they see the dashboards with the data they’re allowed to see. It could look like the following for example:

Someone had a very specific vision on how they wanted to provide the dashboards to the end user. They wanted a sidebar where they could flip through each of the dashboards. It could have been something completely different too. What we will focus on is how we can embed these dashboards into our application regardless of what the host application looks like.

Cumul.io comes with a set of publicly available SDKs. Here I’ll show you what you would do if you were to use the Node SDK. Check out our developer docs to see what other SDKs are available and instructions on how to use them.

Step 1: Generate SSO Tokens For Your End Users

Before you can generate SSO tokens for your end users, you will have to make sure that you create an API key and token in Cumul.io. You can do this from your Cumul.io Profile. It should be the organization owner with access to the integration that creates and uses this API key and token to make the SSO authorization request. Once you’ve done this, let’s first create a Cumul.io client which would be done in the server side of the application:

const Cumulio = require("cumulio");  const client = new Cumulio({   api_key: '<YOUR API KEY>',   api_token: '<YOUR API TOKEN>', });

Now we can create the SSO token for the end user. For more information on this API call and the required fields check out our developer documentation on generating SSO tokens.

let promise = client.create('authorization', {   integration_id: '<THE INTEGRATION ID>',   type: 'sso',   expiry: '24 hours',   inactivity_interval: '10 minutes',   username: '< A unique identifier for your end user >',   name: '< end-user name >',   email: '< end-user email >',   suborganization: '< end-user suborganization >',   role: 'viewer',   metadata: {} });

Here, notice how we have added the optional metadata field. This is where you can provide the parameters and values with which you want to filter the dashboards’ datasets on. In the example we’ve been going through we’ve been filtering based on department so we would be adding this to the metadata. Ideally you would get this information from the authentication provider you use. See a detailed explanation on how we’ve done this with Auth0.

This request will return a JSON object that contains an authorization id and token which is later used as the key/token combination to embed dashboards in the client-side.

Something else you can optionally add here which is pretty cool is a CSS property. This would allow you to define custom look and feel for each user (or user group). For the same application, this is what the Marketing Dashboard could look like for Angelina vs Brad:

Step 2: Embed

We jumped ahead a bit there. We created SSO tokens for end users but we haven’t yet actually embedded the dashboards into the application. Let’s have a look at that. First up, you should install and import the Web component.

import '@cumul.io/cumulio-dashboard';

After importing the component you can use it as if it were an HTML tag. This is where you will embed your dashboards:

<cumulio-dashboard   dashboardId="< a dashboard id >"   dashboardSlug="< a dashboard slug >"   authKey="< SSO key from step 1 >"   authToken="< SSO token from step 1 >"> </cumulio-dashboard>

Here you will have a few options. You can either provide the dashboard Id for any dashboard you want to be embedding, or you can provide the dashboard slug which we defined in the Integration setup (which is why I highly recommend this, it’s much more readable doing it this way). For more detailed information on how to embed dashboards you can also check out our developer documentation.

A nice way to do this step is of course just defining the skeleton of the dashboard component in your HTML file and filling in the rest of it from the client side of your application. I’ve done the following, although it’s of course not the only way:

I’ve added the dashboard component with the ID dashboard:

<cumulio-dashboard id="dashboard"></cumulio-dashboard>

Then, I’ve retrieved this component in the client code as follows:

const dashboardElement = document.getElementById("dashboard");

Then I request the SSO token from the server side of my application which returns the required key and token to add to the dashboard component. Let’s assume we have a wrapper function getDashboardAuthorizationToken() that does this for us and returns the response from the server-side SSO token request. Next, we simply fill in the dashboard component accordingly:

const authorizationToken = await getDashboardAuthorizationToken(); if (authorizationToken.id && authorizationToken.token) {   dashboardElement.authToken = authorizationToken.token;   dashboardElement.authKey = authorizationToken.id;   dashboardElement.dashboardSlug = "marketing|sales|leads"; }

Notice how in the previous steps I chose to define slugs for my dashboards that are a part of this integration. This means I can avoid looking up dashboard IDs and adding dashboardId as one of my parameters of the dashboardElement. Instead I can just provide one of the slugs marketing, sales or leads and I’m done! Of course you would have to set up some sort of selection process to your application to decide where and when you embed which dashboard.

That’s it folks! We’ve successfully created an Integration in Cumul.io and in a few lines of code, we’ve been able to embed its dashboards into our application 🎉 Now imagine a scenario where you have to maintain multiple applications at once, either for within the same company or separate ones. Whatever your scenario, I’m sure you can imagine how if you have a number of dashboards where each of them have to go to different places and each of them have to have different access rights depending on where they are and on and on we go.. How it can quickly get out of hand. Integrations allow you to manage this in a simple and neat way, all in one place, and as you can see, mostly from within the Cumul.io UI.

There’s a lot more you can do here which we haven’t gone through in detail. Such as adding user specific custom themes and CSS. We also didn’t go through how you would set parameters and filters in dashboards, or how you would use them from within your host application so that you have a multi-tenant setup. Below you can find some links to useful tutorials and documentation for these steps if you are interested.


The post Embedded Analytics Made Simple With Cumul.io Integrations appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,

How I Made a Generator for SVG Loaders With Sass and SMIL Options

While learning Vue.js, I started building free web tools that involved the exploration of SVG, with the goal of learning something about both! Let’s take a look at one of those tools: a generator that makes SVG loaders and lets you choose between SMIL or Sass animation, different styles, colors, shapes, and effects. It even lets you paste in a custom path or text, and then download the final SVG, copy the code, or open a demo over at CodePen.

How it started

Three coincidences led me to build a generator for SVG loaders.

Coincidence 1: Sarah Drasner’s book

The first time I read about Sass loops was in Sarah Drasner’s SVG Animations. She shows how to stagger animations with a Sass function (like the does in Chapter 6, “Animating Data Visualizations”).

I was inspired by that chapter and the possibilities of Sass loops.

Coincidence 2: A GIF

At that same point in life, I was asked to replicate a “loader” element, similar to Apple’s old classic.

A round segmented spinner where each segment fades in and out in succession to create a circling effect.
This is a mockup of the loader I was asked to make.

I referenced Sarah’s example to make it happen. This is the Sass loop code I landed on:

@for $ i from 1 to 12 {   .loader:nth-of-type(#{$ i}) {     animation: 1s $ i * 0.08s opacityLoader infinite;   } } @keyframes opacityLoader {  to { opacity: 0; } }

This defines a variable for a number (i) from 1 to 12 that increases the delay of the animation with every :nth-child element. It was the perfect use case to animate as many elements as I wanted with only two lines of Sass, saving me CSS declarations for each of the delays I needed. This is the same animation, but written in vanilla CSS to show the difference:

.loader:nth-of-type(1) {   animation: 1s 0.08s opacityLoader infinite; } .loader:nth-of-type(2) {   animation: 1s 0.16s opacityLoader infinite; }  /* ... */  .loader:nth-of-type(12) {   animation: 1s 0.96s opacityLoader infinite; } @keyframes opacityLoader {   to { opacity: 0; } }

Coincidence 3: An idea

With these things going on in my head, I had an idea for a gallery of loaders, where each loader is made from the same Sass loop. I always struggle to find these kinds of things online, so I thought it might be useful for others, not to mention myself.

I had already built this kind of thing before as a personal project, so I ended up building a loader generator. Let me know if you find bugs in it!

One loader, two outputs

I was blocked by my own developer skills while creating a generator that produces the right Sass output. I decided to try another animation approach with SMIL animations, and that’s what I wound up deciding to use.

But then I received some help (thanks, ekrof!) and got Sass to work after all.

So, I ended up adding both options to the generator. I found it was a challenge to make both languages return the same result. In fact, they sometimes produce different results.

SMIL vs. CSS/Sass

I learned quite a bit about SMIL and CSS/Sass animations along the way. These are a few of the key takeaways that helped me on my way to making the generator:

  • SMIL doesn’t rely on any external resources. It animates SVG via presentation attributes directly in the SVG markup. That’s something that neither CSS nor Sass can do.
  • SMIL animations are preserved when an SVG is embedded as an image or as a background image. It is possible to add a CSS <style> block directly inside the SVG, but not so much with Sass, of course. That’s why there is an option to download the actual SVG file when selecting the SMIL option in the generator.
  • SMIL animations look a bit more fluid. I couldn’t find the reason for this (if anyone has any deeper information here, please share!). I though it was related to GPU acceleration, but it seems they both use the same animation engine.
Two spinners, one left and one right. They are both red and consist of circles that fade in and out in succession as an animated GIF.
SMIL (left) and Sass (right)

You might notice a difference in the chaining of the animations between both languages:

  • I used additive="sum" in SMIL to add animations one after the other. This makes sure each new animation effect avoids overriding the previous animation.
  • That said, in CSS/Sass, the W3C points out that [when] multiple animations are attempting to modify the same property, then the animation closest to the end of the list of names wins.

That’s why the order in which animations are applied might change the Sass output.

Working with transforms

Working with transformations in the loader’s styling was a big issue. I had applied transform: rotate inline to each shape because it’s a simple way to place them next to each other in a circle and with a face pointing toward the center.

<svg>   <!-- etc. -->   <use class="loader" xlink:href="#loader" transform="rotate(0 50 50)" />   <use class="loader" xlink:href="#loader" transform="rotate(30 50 50)" />   <use class="loader" xlink:href="#loader" transform="rotate(60 50 50)" />   <!-- etc. --> </svg>

I could declare a type in SMIL with <animateTransform> (e.g. scale or translate) to add that specific transform to the original transformation of each shape:

<animateTransform   attributeName="transform"   type="translate"   additive="sum"   dur="1s"   :begin="`$ {i * 0.08}s`"   repeatCount="indefinite"   from="0 0"   to="10" />

But instead, transform in CSS was overriding any previous transform applied to the inline SVG. In other words, the original position reset to 0 and showed a very different result from what SMIL produced. That meant the animations wound up looking identical no matter what.

The same two red spinners as before but with different results. The SMIL version on the left seems to work as expected but the Sass one on the right doesn't animate in a circle like it should.

The (not very pretty) solution to make the Sass similar to SMIL was to place each shape inside a group (<g>) element, and apply the inline rotation to the groups, and the animation to the shapes. This way, the inline transform isn’t affected by the animation.

<svg>   <!-- etc. -->   <g class="loader" transform="rotate(0 50 50)">     <use xlink:href="#loader" />   </g>   <g class="loader" transform="rotate(30 50 50)">     <use xlink:href="#loader" />   </g>   <!-- etc. --> </svg>

Now both languages have a very similar result.

The technology I used

I used Vue.js and Nuxt.js. Both have great documentation, but there are more specific reasons why I choose them.

I like Vue for lots of reasons:

  • Vue encapsulates HTML, CSS, and JavaScript as a “single file component” where all the code lives in a single file that’s easier to work with.
  • The way Vue binds and dynamically updates HTML or SVG attributes is very intuitive.
  • HTML and SVG don’t require any extra transformations (like making the code JSX-compatible).

As far as Nuxt goes:

  • It has a quick boilerplate that helps you focus on development instead of configuration.
  • There’s automatic routing and it supports auto-importing components.
  • It’s a good project structure with pages, components, and layouts.
  • It’s easier to optimize for SEO, thanks to meta tags.

Let’s look at a few example loaders

What I like about the end result is that the generator isn’t a one-trick pony. There’s no one way to use it. Because it outputs both SMIL and CSS/Sass, there are several ways to integrate a loader into your own project.

Download the SMIL SVG and use it as a background image in CSS

Like I mentioned earlier, SMIL features are preserved when an SVG is used as a background image file. So, simply download the SVG from the generator, upload it to your server, and reference it in CSS as a background image.

Similarly, we could use the SVG as a background image of a pseudo-element:

Drop the SVG right into the HTML markup

The SVG doesn’t have to be a background image. It’s just code, after all. That means we can simply drop the code from the generator into our own markup and let SMIL do its thing.

Use a Sass loop on the inline SVG

This is what I was originally inspired to do, but ran into some roadblocks. Instead of writing CSS declarations for each animation, we can use the Sass loop produced by the generator. The loop targets a .loader class that’s already applied to the outputted SVG. So, once Sass is compiled to CSS, we get a nice spinning animation.

I’m still working on this

My favorite part of the generator is the custom shape option where you can add text, emojis, or any SVG element to the mix:

The same circle spinner but using custom SVG shapes: one a word, one a poop emoji, and bright pink and orange asterisk.
Custom text, emoji, and SVG

What I would like to do is add a third option for styles to have just one element where you get to work with your own SVG element. That way, there’s less to work with, while allowing for simpler outputs.

The challenge with this project is working with custom values for so many things, like duration, direction, distance, and degrees. Another challenge for me personally is becoming more familiar with Vue because I want to go back and clean up that messy code. That said, the project is open source, and pull requests are welcome! Feel free to send suggestions, feedback, or even Vue course recommendations, especially ones related to SVG or making generators.

This all started with a Sass loop that I read in a book. It isn’t the cleanest code in the world, but I’m left blown away by the power of SMIL animations. I highly recommend Sarah Soueidan’s guide for a deeper dive into what SMIL is capable of doing.

If you’re curious about the safety of SMIL, that is for good reason. There was a time when Chrome was going to entirely deprecated SMIL (see the opening note in MDN). But that deprecation has been suspended and hasn’t (seemingly) been talked about in a while.

Can I use SMIL?

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
5 4 No 79 6

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
92 90 3 6.0-6.1

The post How I Made a Generator for SVG Loaders With Sass and SMIL Options appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Mistakes I’ve Made as an Engineering Manager

I’ve been a manager for many years at companies of different scale. Through these experiences, I’ve done my share of learning, and made some mistakes along that way that were important lessons for me. I want to share those with you.

But before diving in, I want to mention a strong caveat that my advice may be unique to my situation because I’m white and a woman in tech. My experiences may be relevant to that point of view, but your mileage may vary.

Another huge caveat: I’m sharing mistakes I’ve made so far in the interest of helping others, but I’m sure I’m not done making mistakes, either. I don’t have it all figured it out, I’m still on this journey.

Credit: WoCinTechChat

Mistake 1: Thinking people give feedback the way they want to receive it

Feedback is one of the most important tools you have as a manager, but it can also be incredibly disruptive with poor execution. One of the hardest things I’ve had to learn is that humans aren’t pure functions: you can put a form input in front of them one day and get one result, then again another day and get an entirely different result.

The same is true of how people give and receive feedback: someone may give you feedback in a particular way, but they prefer to receive much differently when it comes to themselves.

How do you get around this? Asking helps. I’ve started doing an exercise with my team where I ask the group as a whole how they would like to get feedback. Not only does it open up ideas, but it also helps that each individual has to think for themselves how they prefer to receive feedback. Normalizing this type of vulnerability and self-reflection can help us all feel like partners, instead of some top-down edict.

Another thing that’s helped? Asking folks directly in a one-on-one meeting if they have feedback for me as a manager, and following up with an anonymous survey. Again, it makes things feel less one-sided and provides everyone the opportunity to say things that they might not want to say directly to my face, which I know can be tough.

And lastly, if something comes up, addressing it immediately can be helpful. There’s nothing worse than your manager having an issue with something you did and only finding out about it three months later, especially if it’s tied to a performance review that you could have impacted had they been transparent earlier.

The truth is that even my advice here is imperfect. Feedback is tough. Being honest and improving together as a team is awkward. It’s incredibly worth it, though. That’s where the real growth is. That said, no two people are alike, no two groups are alike, and you may have to use your best judgement given the situation at hand.

Mistake 2: Trying to do everything yourself as a manager is the best way to help

Years ago, I managed a woman who was bright, talented, capable, and an all around pleasure. She was sort of new to the industry and could come across as timid, so I did my best to be a poop umbrella for her, fighting battles behind the scenes to set her up for success. She was on a steady track to land a senior role. Even after I decided to leave the company, I let the next manager know this person is track for a senior position in the next few months.

Then I moved to another city. Years later, I met up with the woman and was shocked to learn she never got the position.

Here’s what I learned: her promotion wasn’t the same high priority for the capable hands I left her in as it was for me. The team was challenged with a million other things that took center stage to the extent that her promotion fell off the radar. But even more than that, what became very clear to me was that all of that “protection” I thought I had set up for her didn’t really serve her well for the long haul. For example, I didn’t teach her how to advocate for herself or how to navigate the system. I vowed never to make that mistake again.

This is tough! If you’re strong and care about your team as people, it can feel very unnatural to teach someone to advocate instead of moving things out of their way themselves. And the point is not to throw that person into the fire. The point is to care. Are you teaching the things they need to learn? Are they really growing under you? Feeling like you’re protecting someone at all costs also lead to your own ego trip, too, which threatens progress.

Try to think through what skills someone needs to succeed without you. Teach those things incrementally. Sure, this is easy advice to say, but it’s really hard to do in the thick of things. Spend some time with it, and think through ways you can inject that learning into everyday work and interactions.

Credit: Charles Deluvio on Unsplash

Mistake 3: Communicating something one time is enough

No one likes to feel like they’re repeating themselves. It’s annoying to say someone more than once, and it’s annoying to hear something over and again. But if you have a big enough group and there’s enough going on, things are going to slip through the cracks, so repetition becomes an important tool to make things stick. The trick is to say the same things, but in different ways.

There was a time last year when I asked my team to do something and none of them did it. What happened there? Given that it’s a team of highly efficient, strong collaborators, do you think they just all table-flipped and didn’t take action? Not a chance. I was the one who wasn’t clear. In fact, you can probably guess that if a whole group of people don’t understand or take action, the chance is that you, the manager are the common denominator for why something is blocked. Not only did I not repeat myself enough to be clear, I didn’t align anyone with the why of the purpose of the task. It’s pretty easy to forget or not prioritize doing something if you have no clue why you’re doing it. Repeat yourself and align the group with the importance of the task and you’ll likely have a better result.

Think of all the ways we have to communicate these days: chats, emails, video meetings, texts, document comments, and so much more. And because some people communicate better in one medium than another, using all of the platforms have in various mediums becomes a strategy for repetition without nagging.

I’ve found that what work best is allowing everyone to own the information themselves. For example, if your team practices career laddering, each person they read the ladders aloud in a one-on-one and then talk you through their responses to each item. That way, you’re not lecturing — they are owning where they are and what the next steps are as you guide them along.

Mistake 4: You have to have everything together all the time

Some folks think that management looks like a steel fortress of preparedness and authority. I’m not so sure about that.

If something goes wrong, are you more likely to tell the manager acts as though they have everything together all the time, or the manager owns their mistakes? The truth is that your team needs to know you’re human. You can’t fix problems if you don’t know about them, and no one will tell you about them unless you make space for that.

One time, the night before a big release, someone on the team pushed a change that created thousands upon thousands of calls to a service that, in turn, thought it was the target of a DDoS attack, which then shut down our access. Here’s a moment when a lot of folks could have panicked and blamed one another. Instead, we giggled wildly, jumped into chat and on calls, fixed it, and kept going.

I couldn’t have been more proud of the team that day. Their response was wonderful. And it makes all the difference in how we work together, recover, and iterate.

You’re the manager. You have to be show your vulnerability first. You can try this by admitting you’re having a bad day, that you don’t understand something, or made a mistake.


Being a manager is tough. Your mistakes impact people. I’ve made all of the mistakes above and more. I feel that it’s critical to share and learn from one another, so when we encounter pitfalls, we don’t feel alone and know a path forward.


The post Mistakes I’ve Made as an Engineering Manager appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

A Complete State Machine Made With HTML Checkboxes and CSS

State machines are typically expressed on the web in JavaScript and often through the popular XState library. But the concept of a state machine is adaptable to just about any language, including, amazingly, HTML and CSS. In this article, we’re going to do exactly that. I recently built a website that included a “no client JavaScript” constraint and I needed one particular unique interactive feature.

The key to all this is using <form> and <input type="radio"> elements to hold a state. That state is toggled or reset with another radio <input> or reset <button> that can be anywhere on the page because it is connected to the same <form> tag. I call this combination a radio reset controller, and it is explained in more detail at the end of the article. You can add more complex state with additional form/input pairs.

It’s a little bit like the Checkbox Hack in that, ultimately, the :checked selector in CSS will be doing the UI work, but this is logically more advanced. I end up using a templating language (Nunjucks) in this article to keep it manageable and configurable.

Traffic light state machine

Any state machine explanation must include the obligatory traffic light example. Below is a working traffic light that uses a state machine in HTML and CSS. Clicking “Next” advances the state. The code in this Pen is post processed from the state machine template to fit in a Pen. We’ll get into the code in a more readable fashion later on.

Hiding/Showing table information

Traffic lights aren’t the most practical every-day UI. How about a <table> instead?

There are two states (A and B) that are changed from two different places in the design that affect changes all over the UI. This is possible because the empty <form> elements and <input> elements that hold state are at the very top of the markup and thus their state can be deduced with general sibling selectors and the rest of the UI can be reached with descendent selectors. There is a loose coupling of UI and markup here, meaning we can change the state of almost anything on the page from anywhere on the page.

General four-state component

Diagram of a generic four-state finite state machine

The goal is a general purpose component to control the desired state of the page. “Page state” here refers to the desired state of the page and “machine state” refers to the internal state of the controller itself. The diagram above shows this generic state machine with four states(A, B, C and D). The full controller state machine for this is shown below. It is built using three of the radio reset controller bits. Adding three of these together forms a state machine that has eight internal machine states (three independent radio buttons that are either on or off).

Diagram of the controller’s internal states

The “machine states” are written as a combination of the three radio buttons (i.e. M001 or M101). To transition from the initial M111 to M011, the radio button for that bit is unset by clicking on another radio <input> in the same group. To transition back, the reset <button> for the <form> attached to that bit is clicked which restores the default checked state. Although this machine has eight total states, only certain transitions are possible. For instance, there is no way to go directly from M111 to M100 because it requires two bits to be flipped. But if we fold these eight states into four states so that each page state shares two machine states (i.e. A shares states M111 and M000) then there is a single transition from any page state to any other page state.

Reusable four-state component

For reusability, the component is built with Nunjucks template macros. This allows it to be dropped into any page to add a state machine with the desired valid states and transitions. There are four required sub-components:

  • Controller
  • CSS logic
  • Transition controls
  • State classes

Controller

The controller is built with three empty form tags and three radio buttons. Each of the radio buttons checked attribute is checked by default. Each button is connected to one of the forms and they are independent of each other with their own radio group name. These inputs are hidden with display: none because they are are not directly changed or seen. The states of these three inputs comprise the machine state and this controller is placed at the top of the page.

{% macro FSM4S_controller()%}   <form id="rrc-form-Bx00"></form>   <form id="rrc-form-B0x0"></form>   <form id="rrc-form-B00x"></form>   <input data-rrc="Bx00" form="rrc-form-Bx00" style="display:none" type="radio" name="rrc-Bx00" checked="checked" />   <input data-rrc="B0x0" form="rrc-form-B0x0" style="display:none" type="radio" name="rrc-B0x0" checked="checked" />   <input data-rrc="B00x" form="rrc-form-B00x" style="display:none" type="radio" name="rrc-B00x" checked="checked" /> {% endmacro %}

CSS logic

The logic that connects the controller above to the state of the page is written in CSS. The Checkbox Hack uses a similar technique to control sibling or descendant elements with a checkbox. The difference here is that the button controlling the state is not tightly coupled to the element it is selecting. The logic below selects based on the “checked” state of each of the three controller radio buttons and any descendant element with class .M000. This state machine hides any element with the .M000 class by setting display: none !important. The !important isn’t a vital part of the logic here and could be removed; it just prioritizes the hiding from being overridden by other CSS.

{%macro FSM4S_css()%} <style>   /* Hide M000 (A1) */   input[data-rrc="Bx00"]:not(:checked)~input[data-rrc="B0x0"]:not(:checked)~input[data-rrc="B00x"]:not(:checked)~* .M000  {     display: none !important;   }    /* one section for each of 8 Machine States */  </style> {%endmacro%}

Transition control

Changing the state of the page requires a click or keystroke from the user. To change a single bit of the machine state, the user clicks on a radio button that is connected to the same form and radio group of one of the bits in the controller. To reset it, the user clicks on a reset button for the form connected to that same radio button. The radio button or the reset button is only shown depending on which state they are in. A transition macro for any valid transition is added to the HTML. There can be multiple transitions placed anywhere on the page. All transitions for states currently inactive will be hidden.

{%macro AtoB(text="B",class="", classBtn="",classLbl="",classInp="")%}   <label class=" {{class}} {{classLbl}} {{showM111_A()}} "><input class=" {{classInp}} " form="rrc-form-Bx00" type="radio" name="rrc-Bx00" />{{text}}</label>   <button class=" {{class}} {{classBtn}} {{showM000_A1()}} " type="reset" form="rrc-form-Bx00">{{text}}</button> {%endmacro%} 

State class

The three components above are sufficient. Any element that depends on state should have the classes applied to hide it during other states. This gets messy. The following macros are used to simplify that process. If a given element should be shown only in state A, the {{showA()}} macro adds the states to hide.

{%macro showA() %}   M001 M010 M100 M101 M110 M011 {%endmacro%} 

Putting it all together

The markup for the traffic light example is shown below. The template macros are imported in the first line of the file. The CSS logic is added to the head and the controller is at the top of the body. The state classes are on each of the lights of the .traffic-light element. The lit signal has a {{showA()}} macro while the “off” version of signal has the machine states for the .M000 and .M111 classes to hide it in the A state. The state transition button is at the bottom of the page.

{% import "rrc.njk" as rrc %} <!DOCTYPE html> <html lang="en"> <head>   <meta charset="UTF-8" />   <title>Traffic Light State Machine Example</title>   <link rel="stylesheet" href="styles/index.processed.css">   {{rrc.FSM4S_css()}} </head> <body>   {{rrc.FSM4S_controller()}}   <div>     <div class="traffic-light">       <div class="{{rrc.showA()}} light red-light on"></div>       <div class="M111 M000 light red-light off"></div>       <div class="{{rrc.showB()}} light yellow-light on"></div>       <div class="M100 M011 light yellow-light off"></div>       <div class="{{rrc.showC()}} light green-light on"></div>       <div class="M010 M101 light green-light off"></div>     </div>     <div>       <div class="next-state">         {{rrc.AtoC(text="NEXT", classInp="control-input",           classLbl="control-label",classBtn="control-button")}}         {{rrc.CtoB(text="NEXT", classInp="control-input",           classLbl="control-label",classBtn="control-button")}}         {{rrc.BtoA(text="NEXT", classInp="control-input",           classLbl="control-label",classBtn="control-button")}}       </div>     </div>   </div> </body> </html>

Extending to more states

The state machine component here includes up to four states which is sufficient for many use cases, especially since it’s possible to use multiple independent state machines on one page.

That said, this technique can be used to build a state machine with more than four states. The table below shows how many page states can be built by adding additional bits. Notice that an even number of bits does not collapse efficiently, which is why three and four bits are both limited to four page states.

Bits (rrcs) Machine states Page states
1 2 2
2 4 2
3 8 4
4 16 4
5 32 6

Radio reset controller details

The trick to being able to show, hide, or control an HTML element anywhere on the page without JavaScript is what I call a radio reset controller. With three tags and one line of CSS, the controlling button and controlled element can be placed anywhere after this controller. The controlled side uses a hidden radio button that is checked by default. That radio button is connected to an empty <form> element by an ID. That form has a type="reset" button and another radio input that together make up the controller.

<!-- RRC Controller --> <form id="rrc-form"></form> <label>   Show   <input form="rrc-form" type="radio" name="rrc-group" /> </label> <button type="reset" form="rrc-form">Hide</button>  <!-- Controlled by RRC --> <input form="rrc-form" class="hidden" type="radio" name="rrc-group" checked /> <div class="controlled-rrc">Controlled from anywhere</div>

This shows a minimal implementation. The hidden radio button and the div it controls need to be siblings, but that input is hidden and never needs to be directly interacted with by the user. It is set by a default checked value, cleared by the other radio button, and reset by the form reset button.

input[name='rrc-group']:checked + .controlled-rrc {   display: none; } .hidden {   display: none; }

Only two line of CSS are required to make this work. The :checked pseudo selector connects the hidden input to the sibling it is controlling. It adds the radio input and reset button that can be styled as a single toggle, which is shown in the following Pen:

Accessibility… should you do this?

This pattern works, but I am not suggesting it should be used everywhere for everything. In most cases, JavaScript is the right way to add interactivity to the web. I realize that posting this might get some heat from accessibility and semantic markup experts. I am not an accessibility expert, and implementing this pattern may create problems. Or it may not. A properly labelled button that does something to the page controlled by otherwise-hidden inputs might work out fine. Like anything else in accessibility land: testing is required.

Also, I have not seen anyone else write about how to do this and I think the knowledge is useful — even if it is only appropriate in rare or edge-case situations.


The post A Complete State Machine Made With HTML Checkboxes and CSS appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Web Scraping Made Simple With Zenscrape

Web scraping has always been taken care of by actual developers, since a lot of coding, proxy management and CAPTCHA-solving is involved. However, the scraped data is very often needed by people that are non-coders: Marketers, Analysts, Business Developers etc.

Zenscrape is an easy-to-use web scraping tool that allows people to scrape websites without having to code.

Let’s run through a quick example together:

Select the data you need

The setup wizard guides you through the process of setting up your data extractor. It allows you to select the information you want to scrape visually. Click on the desired piece of content and specify what type of element you have. Depending on the package you have bought (they also offer a free plan), you can select up to 30 data elements per page.

The scraper is also capable of handling element lists.

Schedule your extractor

Perhaps, you want to scrape the selected data at a specific time interval. Depending on your plan, you can choose any time span between one minute to one hour. Also, decide what is supposed to happen with the scraped data after it has been gathered.

Use your data

In this example, we have chosen the .csv-export method and have selected a 10 minute scraping interval. Our first set of data should be ready by now. Let’s take a look:

Success! Our data is ready for us to be downloaded. We can now access all individual data sets or download all previously gathered data at once, in one file.

Need more flexibility?

Zenscrape also offers a web scraping API that returns the HTML markup of any website. This is especially useful for complicated scraping projects, that require the scraped content to be integrated into a software application for further processing.

Just like the web scraping suite, the API does not forward failed requests and takes care of proxy management, Capotcha-solving and all other maintenance tasks that are usually involved with DIY web scrapers.

Since the API returns the full HTML markup of the related website, you have full flexibility in terms of data selection and further processing.

Try Zenscrape

The post Web Scraping Made Simple With Zenscrape appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

How we made Carousell’s mobile web experience 3x faster

Both a sobering and interesting read from Stacey Tay on how the team at Carousell gathered the metrics to define a performance budget and, in turn, developed a better experience for their customers:

Our new PWA listing page loads 3x faster than our old listing page. After releasing this new page, we’ve had a 63% increase in organic traffic from Indonesia, compared to our our all time-high week. Over a 3 week period, we also saw a 3x increase in ads click-through-rates and a 46% increase in anonymous users who initiated a chat on the listing page.

The team inlined critical CSS, reduced the number of resources the app was loading, and implemented a lazy loading strategy, among many other things. I think it’s interesting to note that they also changed the design of the app in certain ways to make things more performant, too. I reckon it’s easy to fall into the trap of thinking that performance is solely a task for developers and posts like this prove that it’s more collaborative than that.

Direct Link to ArticlePermalink

The post How we made Carousell’s mobile web experience 3x faster appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]