and

5 Common WordPress Issues and How to Fix Them

While WordPress is generally user-friendly, some common errors can occur. Fortunately, your WordPress issue has likely been reported and resolved already. We all appreciate WordPress, but it can sometimes be frustrating. It’s unpleasant when our site crashes due to the [...]

Read Article

The post 5 Common WordPress Issues and How to Fix Them first appeared on CSS Reset.




and

iPhone vs Android – Which One’s Better?

I read this line on a website. “Apple diehard fans would be tempted to buy a live pig if it had the familiar logo branded on its backside”. Ha! I wondered what that might look like, so I tried to draw a picture in my mind and this was the outcome. Jokes aside people, I...




and

English language requirement tightened for Student and Temporary Graduate Visa Applicants

The Australian Government has recently announced new English language requirements for Student and Temporary Graduate visa applications. These changes are intended to ensure that international students and graduates have a solid command of the English language to successfully participate in academic and professional settings in Australia. New English Language Requirements Under the updated visa application […]

The post English language requirement tightened for Student and Temporary Graduate Visa Applicants appeared first on Australian Visa Experts.




and

First impressions of the Fuji X-Pro2 (and the Fujinon 100-400mm lens)

Fuji released their new flagship camera this month, the X-Pro2. It is the first X-series camera to feature a 24MP sensor (compared to 16MP before) and it has a very interesting hybrid optical & electronic view finder. When I first […]




and

Old and broken – but oh so beautiful

For years now I’ve had a thing for old doorways with big old locks – now on Cyprus it escalated a bit due to the number of absolutely beautiful old doors. Many of them not restored but broken or run […]




and

Autonomy, failure, and “pushing down”; what I’m learning about leadership

That title is a lot to digest, but as part of my (ongoing and never ending) journey to master effective leadership, it would be irresponsible not to cover the topic of autonomy. Human beings have an innate inner drive to be autonomous, self-determined, and connected to one another. And when that drive is liberated, people …

The post Autonomy, failure, and “pushing down”; what I’m learning about leadership appeared first on Nathan Rice.




and

How (and why) I went from being a night owl to waking up at 5AM

I was born in the mid-80s, so I’m about as “90s kid” as you can possibly be. I grew up loving Nicktoons and TGIF, eating Dunkaroos and drinking Surge. I loved my SNES, but eventually became a Playstation kid. I may or may not have dreamed of being a Ninja Turtle. But when I was …

The post How (and why) I went from being a night owl to waking up at 5AM appeared first on Nathan Rice.




and

7 good habits that stuck (and 3 that I’m still working on)

Habits are strange and beautiful things. They’re sometimes really hard to form or break, and yet they are absolutely key to living a successful life. We simply do not have enough willpower in reserve to do all the things we know we need to do. So, the brain forms habits to offload the task of …

The post 7 good habits that stuck (and 3 that I’m still working on) appeared first on Nathan Rice.




and

12 thought provoking quotes to inspire gratitude and contentment

I love a good quote. There’s just something about finding a sentence or phrase that speaks directly to the part of your brain that needed convincing. So when I see a good quote, something that speaks to me or illuminates a concept that I needed to see more clearly, I take notice and try to …

The post 12 thought provoking quotes to inspire gratitude and contentment appeared first on Nathan Rice.




and

The Basics Of Product Design: Introduction To Key Concepts And Principles

Whether you're a project manager or a startup business owner, most of you lack the time or the in-house experts to help with product design. Product design ...




and

Harry Potter If It Was Made By Wes Anderson

Attention all Muggles and Wizards! ✨🧙‍♂️🔮 If Harry Potter was directed by the one and only Wes Anderson, you ca ...




and

The Impact Of AI Software On Architecture And Design: Revolutionizing Creativity And Efficiency

The emergence of AI software in the field of architecture and design has sparked a significant shift in how professionals approach their work. With advancem ...




and

Embracing The Creative Journey: Ignite Your Passion And Unlock Limitless Potential

Creativity is not a gift that only some people have. It is a skill that can be learned and developed by anyone who is willing to explore new possibilities, ...




and

Harry Potter And The Forbidden Party

Step into a realm where the magical world of Harry Potter collides with the captivating aesthetics of HBO's Euphoria, giving birth to an AI-generated Hogwar ...




and

Personal User Interfaces: A.I. Augmented Reality And The Future Of Personalized Advertising

Technology today loves making stuff just for you, and it's changing the game in how we get our digital kicks. In an era where personalization is paramount, ...




and

The Challenges and Rewards of Responsive Design

During 2012 there were many progressions in the world of web design, and one of the main success stories was the growth and popularity of responsive web design. It’s no wonder either, as we were in need of something like this, to give an ounce of flexibility to traditional design – which is exactly what […]




and

How to Make a Grid in Adobe Illustrator (Rectangular and Polar)

Are you looking for a way to create an effective grid system in Adobe Illustrator? If so, then you've come to the right place! This quick tutorial will teach you how to make a comprehensive and visually appealing grid using the popular vector graphics software. While creating grids manually can be time-consuming, with this tutorial,...

The post How to Make a Grid in Adobe Illustrator (Rectangular and Polar) appeared first on Bittbox.




and

Where is the Options Bar in Photoshop and How to Use It?

One of the best things about Photoshop is its exceptional tool flexibility, simplifying our work and enhancing our productivity. The Options Bar works as a gateway to access all the options associated with the selected tool, enabling seamless customization and control over the tools for us. What is the Options Bar? The Options Bar is...

The post Where is the Options Bar in Photoshop and How to Use It? appeared first on Bittbox.




and

Scale Tool in Illustrator: What It Does and How to Use It 

Ready to add the finishing touches to your design in Adobe Illustrator? But the objects aren’t properly aligned? Does your design look unprofessional because it isn’t adjusted properly? Well, worry no more! Because with just a few steps, this tutorial explains how to use Illustrator's Scale Tool to resize, adjust, and align your design perfectly.  ...

The post Scale Tool in Illustrator: What It Does and How to Use It  appeared first on Bittbox.




and

IconDock and Themify Coupon Code

If you are interested to buy icons from IconDock or themes from Themify, I have a coupon code for you. Use ‘ndesign’ coupon code on IconDock to save 20% off any icon sets. The discount code on Themify is also the same ‘ndesign’. You can save 20% off any WordPress themes.




and

Stuck with Apple’s Aperture? Adobe will give you a hand

After the announcement that Apple will not be continuing development of Aperture, Adobe announces the development of a migration tool to help those thinking about switching.





and

WordPress ecommerce plugins: The top platforms and tools for launching a successful store

Find the best WordPress ecommerce plugins. Compare top options: Are they worth the cost? Proven tools for a profitable online store.




and

Unique value proposition — Why it matters and how to write one 

What makes consumers choose one company over another? Identify your unique value proposition to make more sales at higher prices. Examples & tips.




and

Spain-based DOMMA sees 350% YoY growth with WooCommerce and Google

How the wellness brand DOMMA achieved 350% year-over-year growth with one key tool. Can’t-miss success tips from a successful women-owned brand.




and

Kellox and Maksimer: Speeding up 800,000 SKUs with WooCommerce

How one of Norway's largest importers migrated to WooCommerce and scaled an 800k SKU operation for a better user experience, performance, and results.




and

2024 Black Friday trends: Top strategies from Woo stores and partners

Black Friday trends: Understand the best practices and favorite tools for your WooCommerce store's Black Friday marketing strategy.




and

How to choose an NFT and tokengating partner

How to navigate NFTs + the top considerations for choosing the right partner in this beginners guide for store owners.




and

Prepare your checkout for the holidays: 5 ways to reduce cart abandonment with Amazon Pay

Make the most of the holiday shopping season — convert more visitors into customers. Quick changes that can make a big difference in 5 steps.




and

Summer paddling, swimming and photography on Horsetooth Reservoir

Stand up paddling During August 2023 I visited Horsetooth Reservoir several times with Starboard Waterline SUP, the same board I paddled in the Missouri River 340 race. It was rather a relaxed paddling with a lot of photography since I […]




and

Florida to Colorado with 4Runner and Liteboat Rowing Shell

In late November, I embarked on an 8-day trip from Colorado to Florida and back. The primary goal was to bring home a rowing shell and, of course, capture as many pictures and video clips as possible. I drove my […]











and

No longer an Underdog, Sony cameras and lenses win multiple categories at TIPA 2022 World Awards

The Technical Image Press Association, otherwise known as TIPA, has just released its list of...

The post No longer an Underdog, Sony cameras and lenses win multiple categories at TIPA 2022 World Awards appeared first on Alphatracks.





and

Tidy Birds and Neat Bees: on Conscientiousness in Animals

By Mikel Maria Delgado Aeon Human personality theory has long revolved around what we know as the ‘Big Five’ – five dimensions of personality that cover a large swathe of how humans behave across time and contexts. These dimensions are … Continue reading




and

Balancing Image Speed and Quality with imgix

Users expect a website to load fast. An average web page loads in about 2.5 seconds. The longer the user has to wait, the higher the user bounce rate. There are a lot of factors that go into site speed, but images account for about 75% of the page weight on an application or website. Google’s Core Web Vitals uses several metrics to rank sites. Visual site speed or largest contentful paint affects ROI as slower sites have fewer repeat users and fewer sales.

Without properly sized images, both site speed and image quality are affected. Accordingly, we use several methods to deliver properly sized images. Our primary solution is imgix because it is easy to implement and saves managers and clients time and effort. 

Imgix Key Features

Imgix provides a lot of features — some we consistently use on projects, and others we use in very specific situations. 

Responsive Images

Setting up responsive images can be complex. As the variety of devices and their screen resolutions continue to expand, managing all the different image requirements is increasingly challenging.

Consider this scenario: a website’s images look crisp and clear on a high-resolution monitor. When that same page is viewed on a mobile device, the images are so large the page takes forever to load. A solution could be to upload a smaller image for mobile, right? It's not quite that simple. We need eight or more different sizes of the same image to account for different screen sizes and retina screens. Keeping track of all the image sizes and saving each size gets complex and would be nearly impossible to do manually on a site that has hundreds or thousands of images. 

One of our clients, National Park Foundation (NPF), wanted to clarify its message to better target major donors. Their gorgeous, large scenic images are essential to their website strategy.  NPF needed the images throughout their site to be crisp and clear at all screen sizes. Using imgix, NPF content managers can load high-resolution images to the CMS and not worry about the site's speed or performance. They rely on imgix to deliver the correctly resized image for any user's screen.

Imgix does this with their Device Pixel Ratio and Client Hints which automatically sets the pixel density for the image based on the user's device. Those API parameters can be easily set in the URLs for the image’s `srcset`. This allows developers to set an image width for an image `srcset` and then imgix delivers the right pixel ratio image to the user. We use `srcset` with imgix on other client sites like Bezos Earth Fund and Human Rights Campaign as well.

Resize and Cropping

Imgix allows you to crop images in addition to setting an image’s focal point. So, only one image needs to be uploaded and it can be used at multiple sizes and croppings throughout the site. Let's say we upload a large landscape image, but on some pages we only need to use a portion of the image cropped as a square. Imgix will crop the image and deliver the smaller versions on the pages we need while persisting the larger versions on other pages.

At Viget we use resizing and cropping on our own website, including our articles, to crop the staff photo to a smaller size at the top of the articles. This makes it possible for us to upload the staff photo once and the article image gets created automatically. 

Color Palette 

Another feature that we've used on client sites is the Color Palette API which allows you as many colors as you want from an image. For example, the Shedd Aquarium website that we built uses this feature by pulling a vibrant color from the image and setting it as the background color for the page hero. Aside from taking the manual work out of closely aligning photography with a page’s design, there’s an additional benefit: if you are on a slow connection, you will see a dynamic colored banner at the top before the image loads.

Image Upscaling

Another valuable feature is the ability to upscale images. Even if you upload an image that is low resolution, it can still be used at a higher resolution. This is especially useful for e-commerce sites or applications where users are uploading their images.  The upscale feature uses Generative AI to take a pixelated image and create a higher-resolution image. The final image will not be perfect, but it looks more professional. See the upscale demo on imgix.

Non-upscaled image
Upscaled image using Imgix

PDF Preview Images

There is also a PDF page to image option in imgix. The API enables the generation of a page-specific image preview from the PDF, which can serve as both a thumbnail and a full-size preview. When we built AHIP.org, they had a resources section for their members containing quite a few PDFs. To help with clarity and findability, we used imgix to show previews of the PDF documents to non-members. This feature allows AHIP to upload resource PDFs without having to also upload any thumbnail images. 

Face Detection

Another nice feature is a face detection parameter that you can pass to the API. This allows you to upload a photo and no matter the cropping or size it will keep the face as the focal point of the image. We used this feature on NEA for their team member page. It's great for user profile images that are used in different contexts throughout the site. 

Video Streaming

Videos have become a key design element on websites. However, determining which service to use for embedding those videos is an ongoing topic of discussion. Video platforms enable you to integrate the video onto the page, yet they introduce scripts that may impede page loading speed. Imgix offers a video embed service that allows content managers to keep all the images and videos in one place. The videos are automatically encoded to Adaptive Bitrate Streaming to get the best compression and video quality. So, videos load fast and look great on mobile and desktop. 

Imgix Video API with Adaptive Bitrate Streaming

Performance 

A lot of CMSs have image transformations built in. An image transformation encompasses everything for that image including responsive sizes, cropping, resizing, and face detection from the original. This is great for small sets of images, but transformations quickly get out of hand the more images there are on a site. For example, the homepage of the National Park Foundation has a minimum of 96 image transformations. 

Processing all of those image transformations uses a lot of server resources. Imgix saves the images and delivers them through their CDN. The imgix image CDN has an average of 0.15 milliseconds return on requests which enables images to load as quickly as possible. The CDN also caches the images on CDN edge nodes making them immediately available for future requests. 

Flexibility

Imgix is flexible enough to work with almost any site structure; including WordPress, Craft CMS, Shopify, React, Ruby on Rails, Python, and more (see the full list). So, whether a site is a WordPress site or a Rails application, imgix fits right into the ecosystem. And, even better: you don’t have to rebuild your web app or website to gain the benefits of image performance, which can save you a lot of time and money.

Setting up imgix on an existing website is easy. Imgix can connect to existing asset storage sources like AWS, Azure, or a web folder on the same domain. Once the image source is set up, a developer can start passing parameters to the API

Cost

Imgix is free for 1,000 images which makes it easy to integrate and grow with your site. Pricing goes to $750/yr for 5,000 images and $3,000/yr for 25,000 images. In their pricing structure, “images” are categorized as origin images, so the count only includes original images and not transformed ones. So, you could have hundreds of images and thousands of image transformations all being delivered through a CDN for free.

Conclusion

Ensuring fast site speed isn't just important. It is vital. It's the cornerstone of a successful online presence, directly influencing search engine rankings, user satisfaction, and ultimately, your return on investment. Properly sized and optimized images are key to ensuring your site loads quickly and displays correctly for users across various devices.

We have found that imgix enables our team to efficiently create projects with diverse image options, saving managers and developers valuable time. Our clients benefit from reduced server space and an increased site speed. Imgix’s API is comprehensive, so you can use one tool for all features and options around site images — from cropping and resizing to face detection and automated color palettes, to video and beyond. Having used imgix for the past five years to support the wide-ranging needs of our clients, we feel confident recommending it and using it again and again. 

Users expect a website to load fast, and imgix is a reliable way to make sure that happens. 




and

Handling Spelling Mistakes with Postgres Full Text Search

Background #

Postgres Full Text Search (FTS) is a great way to implement site search on a website running Postgres already, without requiring additional infrastructure.

On a recent engagement with a client, we were deciding between Postgres FTS and ElasticSearch. Ultimately we chose FTS because we could spin it up without having to add extra infrastructure, as we would with ElasticSearch.

Since the project was written in Ruby on Rails, we were able to use the excellent PgSearch gem to implement FTS in ActiveRecord.

Multisearch #

As we wanted a general site search, we needed to utilize multisearch. Multisearch combines multiple ActiveRecord models into one search 'document' table that you can search against. For example, if a user searches for some search term, and the search is configured for multisearch, then every single model that we mark as multisearchable will be searched for that term at the same time. See here for more detail.

Search Features #

PgSearch allows for different search features, tsearch, trigram, and dmetaphone. The default is tsearch, which uses the built-in Postgres Full Text Search.

This was great for our use case, since it also comes with highlighting, a feature that was required. The highlighting is from a field returned by Postgres FTS, where it returns the text around the search term for context and bolds the search terms.

Spelling Mistakes #

Unfortunately, tsearch does not handle misspelled words. However, as I mentioned before, PgSearch allows for other search features!

And trigram is a feature that can be installed via a Postgres extension (pg_trgm) that does just that.

Trigram #

  • The idea behind trigram search is to split pieces of text into sets of three-letter segments, and compare the sets to one another
  • If two trigram sets are similar enough, we assume there was a spelling mistake, and return the document with the correctly-spelled term.
  • As a quick example (ignoring whitespace): Consider the word Viget. Viget would make trigrams:
[vig, ige, get]
  • Now, consider our evil twin agency, Qiget. They would make trigrams
[qig, ige, get]
  • The two trigram sets match very closely, with only one of the trigrams not being the same. Thus, if we were to compare these with pg_trgm, we could reasonably tell that anyone typing 'Qiget' must have been actually looking for 'Viget', and just misspelled it.

Working Trigram into our existing solution #

PgSearch allows us to use multiple search features at once, so we can use tsearch and trigram side by side. Note that we cannot just replace tsearch with trigram due to needing some features in tsearch that are exclusive to it. Here is what an example configuration might look like.

PgSearch.multisearch_options = {
  using: {
    tsearch: {
      prefix: true,
      highlight: {
        MaxFragments: 1
      }
    },
    trigram: { 
      only: [:content]
    }
  }
}

Trigram (and timelines) causing issues #

While it was easy to slot Trigram into our multisearch, it caused a pretty serious performance hit. We were seeing 50x-75x slower searches with both features combined than with just tsearch. We needed to find a way to balance performance with handling misspellings

At the point that handling misspellings became prioritized, the entire search feature was almost fully QA'd and about ready to go out. There wasn't much time left in the budget to find a good solution for the issue.

This thread from the PgSearch repo sums it up pretty well – there were multiple other users that were/are having similar issues as we were. The top-rated comment in this thread is someone mentioning that the solution was to just use ElasticSearch ('top-rated' is doing a lot of heavy lifting. It did have the most likes...at two). We needed to find some sort of middle ground solution that we could act on quickly.

Postgres Documentation saves the day #

In the docs for the Trigram Postgres extension, the writers give an idea for using Trigram in conjunction with Full Text Search. The general idea is to create a separate words table that has a Trigram index on it.

Something like this worked for us. Note that we added an additional step with a temporary table. This was to allow us to filter out words that included non-alphabet characters.

execute <<-SQL
  -- Need to make a temp table so we can remove non-alphabet characters like websites
  CREATE TEMP TABLE temp_words AS
    SELECT word FROM ts_stat('SELECT to_tsvector(''simple'', content) FROM pg_search_documents');

  CREATE TABLE pg_search_words (
    id SERIAL PRIMARY KEY,
    word text
  );

  INSERT INTO pg_search_words (word)
    SELECT word
    FROM temp_words
    WHERE word ~ '^[a-zA-Z]+$';
  
  CREATE INDEX pg_words_idx ON pg_search_words USING GIN (word gin_trgm_ops);
  
  DROP TABLE temp_words;
SQL

This words table is therefore populated with every unique word that exists in your search content table. For us, this table was pretty large.

result = ActiveRecord::Base.connection.execute("SELECT COUNT(*) FROM pg_search_words").first['count']
puts result.first['count']
# => 1118644

Keeping the words table up-to-date #

As mentioned in the docs, this table is separate from your search table. Therefore, it needs to be either periodically regenerated or at least have any new words added to search content also added to this table.

One way to achieve this is with a trigger, which adds all new words (still filtering out non-alphabet characters) that are inserted into the documents table to the words table

create_trigger("pg_search_documents_after_insert_update_row_tr", generated: true, compatibility: 1)
  .on("pg_search_documents")
  .after(:insert, :update) do
  <<-SQL_ACTIONS
    CREATE TEMP TABLE temp_words AS
      SELECT word FROM ts_stat('SELECT to_tsvector(''simple'', ' || quote_literal(NEW.content) || ')');

    INSERT INTO pg_search_words (word)
      SELECT word
      FROM temp_words
      WHERE word ~ '^[a-zA-Z]+$';

    DROP TABLE temp_words;
  SQL_ACTIONS

end

Note that this does not handle records being deleted from the table – that would need to be something separate.

How we used the words table #

Assuming for simplicity the user's search term is a single word, if the search returns no results, we compare the search term's trigram set to the trigram index on the words table, and return the closest match.

Then, we'd show the closest match in a "Did you mean {correctly-spelled word}?" that hyperlinks to a search of the correctly-spelled word

Given more time, I would have liked to explore options to speed up the combined FTS and Trigram search. I'm certain we could have improved on the performance issues, but I can't say for sure that we could have gotten the search time down to a reasonable amount.

A future enhancement that would be pretty simple is to automatically search for that correctly-spelled word, removing the prompt to click the link. We could also change the text to something like "Showing results for {correctly-spelled word}".

Ultimately, I think with the situation at hand, we made the right call implementing Trigram this way. The search is just as fast as before, and now in the case of misspellings, a user just has to follow the link to the correctly-spelled word and they will see the results they wanted very quickly.




and

How we use DDEV, Vite and Tailwind with Craft CMS

In 2022 we changed our dev tooling for new Craft CMS projects. Goodbye complex esoteric Webpack configuration, hello Vite. Goodbye complex esoteric Docker Compose configuration, hello DDEV. This small change in tooling has completely transformed our development experience. We start work faster and avoid wasting billable time debugging Webpack and Docker.

From Webpack to Vite #

Webpack has been the defacto way of bundling JavaScript and front end assets. It’s a powerful tool… but with that great power comes great responsibility complexity.

Vite bills itself as the “next generation” of frontend tooling. Vite is much faster at bundling. But more importantly… its default configurations work great for most website projects.

Before (Webpack) #

Well over 300 lines of configuration spanning three files. Good luck making changes!

After (Vite) #

A crisp 30 - 50 lines of code. Want to switch to TypeScript? Need to drop in a popular front-end framework? Easy! All it takes is adding a plugin and 2-3 lines of config.

Deleting old code has never felt this good!

From Docker to DDEV #

Docker is another development staple. It isolates server infrastructure into virtual “containers.” This helps avoid issues that arise from each developer having a slightly different setup. However, Docker can have a learning curve. Config changes, PHP upgrades and unexpected issues often eat up precious project time.

Enter DDEV! DDEV describes itself as “Container superpowers with zero required Docker skills: environments in minutes, multiple concurrent projects, and less time to deployment.” We’ve found that statement to be 100% true.

Before (Docker) #

Every Craft project has a different Docker config. Bugs and upgrades required deep Docker experience. Last (but not least), it was difficult to run several projects at one time (ports often conflict).

After (DDEV) #

Performance is consistently better than our hand-rolled setup thanks to Mutagen and faster DB import/exports. Simultaneous projects run out of the box. DDEV provides (and maintains) a growing list of helpful shortcuts and DX features.

Getting started #

Ready to make the switch? Here’s how to set up DDEV, Vite and Tailwind on your own Craft project.

Show me the config files already! #

If you would rather see full config files instead of following step by step, check out our Craft Site Starter on GitHub.

DDEV #

Let’s set up a fresh DDEV project and start customizing.

  1. Make sure you have DDEV installed on your computer.
  2. If you’re a PHPStorm user, install the exceedingly helpful DDEV plugin. VS Code users have a similar plugin too!
  3. Follow Craft’s guide for creating a new project (they love DDEV too).

Now you have a fresh .ddev/config.yaml just waiting to be customized.

Node Version #

Open your DDEV config and make sure your Node JS version matches Vite’s recommendations.

nodejs_version: '20' # Vite 5 expects Node 18+

Ports for Vite’s dev server #

Next, expose ports that Vite’s dev server uses will use to serve assets.

web_extra_exposed_ports:
  - name: vite
    container_port: 3000
    http_port: 3000
    https_port: 3001

Routing ports can sometimes be confusing. This diagram might help!

  • Vite’s dev server runs inside of DDEV’s web container (a Docker container).
  • Until we expose these extra ports, any custom port within DDEV is unavailable to your host machine (your computer).
  • When it’s time to configure Vite, we’ll use port 3000
  • HTTP and HTTPS traffic must use separate ports.
  • We use port 3000 for http traffic and 3001 for https

Run Vite automatically #

Usually, you’ll want Vite to watch and build files automatically after you start a DDEV project. Using web_extra_daemons adds a separate background process (daemon) for Vite.

web_extra_daemons:
  # Run Vite in a separate process
  - name: 'vite'
    command: 'npm install && npm run dev'
    directory: /var/www/html

Use hooks to improve DX #

DDEV’s powerful hooks system can run tasks before or after various DDEV commands. These post-start tasks keep dependencies and schemas up to date every time you start DDEV.

hooks:
  post-start:
    - composer: install # Keeps installed packages up to date
    - exec: ./craft up # Apply migrations & project config changes

Time for Vite #

Vite is a Node app that’s installed with NPM. Your project will need a package.json. If you don’t have one set up yet, follow NPMs initialization script.

ddev npm init

# Don't forget to ignore node_modules!
echo node_modules >> .gitignore

????Why ddev at the start of the command? This let’s us run NPM from within DDEV’s Docker containers. This means you’ll always be using the Node version configured for this project. DDEV has a bunch of shortcuts and aliases for running CLI commands (such as npm, yarn, craft and composer).

Make sure your NPM package is configured for ES Modules #

Our various config files will be using ES Module syntax for imports and exports.

ddev npm pkg set type=module

Install Vite! #

ddev npm install --save-dev vite

Add convenience scripts to package.json #

"scripts": {
  "dev": "vite",
  "build": "vite build"
}

npm run dev runs Vite in dev mode. It watches and builds your files every save. Files are served through Vite’s dev server.

npm run build bundles your JavaScript, CSS and static images for production. Your deploy process will usually call this script.

Configure vite.config.js #

Running Vite for a server rendered CMS requires some extra configuration. These options put production files in the right spot and keeps Vite’s dev server running on a specific port.

import { defineConfig, loadEnv } from 'vite'

// Match ports in .ddev/config.yaml and config/vite.php
const HTTP_PORT = 3000
const HTTPS_PORT = 3001

export default defineConfig(({ command, mode }) => {
  const env = loadEnv(mode, process.cwd(), '')

  return {
    // In dev mode, we serve assets at the root of https://my.ddev.site:3000
    // In production, files live in the /dist directory
    base: command === 'serve' ? '' : '/dist/',
    build: {
      manifest: true,
      // Where your production files end up
      outDir: './web/dist/',
      rollupOptions: {
        input: {
          // The entry point for Vite, we'll create this file soon
          app: 'src/js/app.js',
        },
      },
    },
    server: {
	    // Special address that respond to all network requests
      host: '0.0.0.0',
	    // Use a strict port because we have to hard code this in vite.php
      strictPort: true,
      // This is the port running "inside" the Web container
      // It's the same as continer_port in .ddev/config.yaml
      port: HTTP_PORT,
      // Setting a specific origin ensures that your fonts & images load
      // correctly. Assumes you're accessing the front-end over https
      origin: env.PRIMARY_SITE_URL + ':' + HTTPS_PORT,
    },
  }
})

Add JavaScript and CSS files (Entrypoint) #

Vite needs an entry point to determine what JavaScript, CSS and Front End assets it needs to compile. Remember src/js/app.js that we defined in vite.config.js? Let's make that file now.

/* Make a file in src/js/app.js */

import '../css/app.css'

console.log('Hello Craft CMS')

We’ll also add our CSS as an import in app.js . In plain-old-JavaScript you can’t import CSS files. However, Vite uses this to figure out CSS dependencies for the project.

Once Vite builds everything for production, you end up with a separate CSS file. The Craft Vite plugin includes this automatically with along your JavaScript bundle.

/* Make a file in src/css/app.css */

body {
	background-color: peachpuff;
}

Install the Vite Craft Plugin #

ddev composer require nystudio107/craft-vite
ddev craft plugin/install vite

Vite assets have different URLs in dev mode vs. production. In dev mode, assets are served from Vite’s dev server. It uses the ports that we defined in our DDEV & Vite configs.

When Vite builds for production, filenames are hashed (app.js becomes app-BZi_KJSq.js). These hashes change when the contents of the file changes. Browser can cache these files indefinitely. When an asset changes, a whole new file is served.

To help find these hashed filenames, Vite creates a manifest.json file. The manifest associates the name of your asset src/js/app.js to the hashed file that ends up on your server web/dist/assets/app-BZi_KJSq.js

The Craft Vite Plugin by NYStudio107 takes care of all this routing for you.

{
  "src/js/app.js": {
    "file": "assets/app-BZi_KJSq.js",
    "name": "app",
    "src": "src/js/app.js",
    "isEntry": true,
    "css": ["assets/app-BXePGY5I.css"]
  }
}

Configure the Vite Craft Plugin #

Make a new plugin config file in config/vite.php

<?php

use crafthelpersApp;

// Use the current host for dev server requests. Otherwise fall back to the primary site.
$host = Craft::$app->getRequest()->getIsConsoleRequest()
    ? App::env('PRIMARY_SITE_URL')
    : Craft::$app->getRequest()->getHostInfo();

return [
    'devServerPublic' => "$host:3001", // Matches https_port in .ddev/config.yaml
    'serverPublic' => '/dist/',
    'useDevServer' => App::env('CRAFT_ENVIRONMENT') === 'dev',
    'manifestPath' => '@webroot/dist/.vite/manifest.json',
    // Optional if using React or Preact
    // 'includeReactRefreshShim' => true,
];

Include your Vite bundles in Twig #

The script and asset functions includes the appropriate files depending on in if you’re in dev mode or production. Clear out your templates/index.twig file and add the following snippet to your <head> tag.

{# Load our main CSS file in dev mode to avoid FOUC #}
{% if craft.vite.devServerRunning() %}
    <link rel="stylesheet" href="{{ craft.vite.asset("src/css/app.css") }}">
{% endif %}

{{ craft.vite.script('src/js/app.js', false) }}

Whew! ???? We’re at a point now where we can test our integration. Run ddev restart and then ddev launch . You should see “Hello Craft CMS” in your browser console.


Setup Tailwind #

Now that Vite is processing src/css/app.css, it’s time to install Tailwind and really get cooking.

These steps are based on Tailwind’s official installation guide. But make sure to run all commands from within DDEV.

Install packages #

ddev npm install -D tailwindcss postcss cssnano autoprefixer
# No DDEV shortcut for npx :(
ddev exec npx tailwindcss init -p

Configure template paths in tailwind.config.js #

/** @type {import('tailwindcss').Config} */
export default {
	// Watch Twig templates and any JS or JSX that might use Tailwind classes.
  content: ['./templates/**/*.twig', './src/**/*.{js,jsx,ts,tsx,svg}'],
  theme: {
    extend: {},
  },
  plugins: [],
}

Configure postcss.config.js for production #

export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
    ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {})
  }
}

Add Tailwind directives to src/css/app.css #

@tailwind base;
@tailwind components;
@tailwind utilities;

You’ll most likely need to run ddev restart again to get Vite to recognize your new Tailwind config.


❓ Do i need to set up live reload of Twig? Turns out it’s already done for you! Styling a Tailwind project means editing Twig files to change styles. It’s super handy to reload your browser every time you save. Normally you’d reach for vite-plugin-restart to get this functionality. However, Tailwind’s JIT mode automatically notifies Vite when CSS has compiled and the page should reload.

That's a wrap! #

That’s all it takes to configure a minimal DDEV and Vite project! We’ve found that both of these tools are easy to extend as a project get more complo'ex. Adding things like Redis or React are just a plugin install and a few lines of config away.

???? If you'd like to see this setup (and more) in a real-world Craft CMS project, check out our Craft Site Starter on GitHub.

Go forth and Vite + DDEV to your heart’s desire.




and

What is a Headless CMS and When Should I Use One?

When starting a new project, decision makers are faced with the dilemma of choosing a content management system (CMS). Sometimes, it’s not that simple and they must choose whether to go with a traditional CMS or a headless CMS. Both offer unique benefits and cater to different needs, making it crucial to understand when each makes sense for your project. Let’s take a look at some considerations that can help you make the right decision.

What is a Traditional CMS?

Traditional CMS’s – like Craft CMS, WordPress, and Drupal – offer a pre-packaged solution for content creation, management, and delivery. They include powerful interfaces with content editing capabilities and templating out of the box, enabling you to create sites with ease. A traditional CMS can be monolithic because the back-end and front-end are tightly coupled. Using a traditional CMS typically means you are using all of the tools included to achieve your goal.

What is a Headless CMS?

A Headless CMS is like a Traditional CMS in that it includes content creation and management tools. But it differs in the fact that the back-end content management system is decoupled from the front-end (presentation layer), allowing developers to use any technology stack they prefer for building the front-end of the site. The back-end acts as an API with its only purpose being to serve content from the database. There are CMS options like ContentfulPayload, and Strapi that are built to be headless. Popular traditional CMS’s like Craft CMS and WordPress also offer headless variants.

The Restaurant Analogy

Let’s simplify things a bit more and look at the decision using an analogy; a restaurant.

Traditional Restaurant (Traditional CMS)

Imagine a restaurant where the kitchen and dining room are connected. The chefs cook the food, and the waitstaff serve it directly to the customers in the same building. This setup means that everything is closely integrated, and the kitchen (back-end) is tightly coupled to the dining experience (front-end). Picture a scenario where the restaurant decides to change from table service to buffet style. The food now needs to be prepared in advance and delivered to the front of house in a different way, potentially even requiring new equipment. The restaurant needs to be reconfigured to not only accommodate the buffet but also to interface with the kitchen differently. Because the restaurant and kitchen are coupled, both sides would require work in order to accommodate a shift in strategy. 

Ghost Kitchen (Headless CMS)

Now, think of a ghost (or cloud) kitchen where food is prepared centrally but can be delivered to various locations or dining experiences. The kitchen (back-end) focuses solely on cooking (content creation and management) and doesn't worry about where the food is served. Instead, the meals (content) can be delivered to different endpoints like food trucks, home deliveries, or partner restaurants (or in our case websites, mobile apps, etc.). This separation allows more flexibility in how and where the content is delivered without changing the core cooking process. If a new experience requires new equipment or processes, the kitchen can be expanded without affecting the front-end experience.

When to Use a Headless CMS

Omni-Channel Content Delivery 

If you consistently need to deliver content across multiple platforms (websites, mobile apps, IoT devices), a headless CMS is ideal because it can serve the same content through APIs to any front-end. The front-end can be swapped out without any need for development to the back-end.

Scalability and Flexibility

If you want the ability to keep your content management system up-to-date independently of the presentation layer, a headless CMS can allow for more agile and scalable development. This could be especially useful if you anticipate needing to redesign or update parts of the front-end frequently without affecting the back-end content.

Front-end Framework Preferences

Maybe your team has developers who are very proficient in a particular JavaScript framework, like Next.js, SvelteKit, or Astro. The time needed to learn a new templating language could push you past your deadline. Maybe you have some cool interactive interface in mind? A headless CMS can provide the raw content for your developers to build highly custom, tailor-made front-ends in whatever language or framework they please.

Security

Going headless can offer security advantages due to its decoupled nature. By communicating via API to the front-end, data access can be controlled more granularly. Because the back-end is only responsible for content management and delivery, fewer plugins are typically used which means a smaller chance of vulnerabilities due to third-party software.

Hosting & Infrastructure

A cloud-based headless CMS offers additional advantages over a self-hosted headless CMS. It can simplify maintenance and operating costs since the cloud provider is responsible for updates and security of the platform. Cloud-based solutions like Strapi Cloud often come with integrated security features, automatic backups, and disaster recovery options.

Which will you choose?

While the flexibility and security a headless CMS offers may be great benefits, it may not be necessary for every project and could even introduce complexity. It’s important to consider the long-term purpose of the project and who will be responsible for maintaining it as well as authoring content. If your primary focus is on managing and delivering content in a structured manner with rapid development, a traditional CMS can be an excellent choice. But if you feel any of the examples I’ve laid out above align with your project’s requirements then a headless CMS may be right for you! 

Whatever route you take, remember that both Craft CMS and WordPress can be used in traditional or headless applications and are a fine choice either way! Now you know the differences between a traditional and headless CMS, and an informed decision can be made. If you have more questions or a project you think could benefit from a traditional or headless CMS, we’d love to help!




and

Setting up a Python Project Using asdf, PDM, and Ruff

When I was tasked with looking into alternative ways to set up a new Python project (not just using the good ol' pip and requirements.txt setup), I decided to try to find the tools that felt best to me, as someone who writes Python and Ruby. On this journey, I found a way to manage dependencies in Python that felt as good as bundler, among other great tools.

The Runtime Version Manager #

asdf has been my primary tool of choice for language version management for multiple years now. The ease of adding plugins and switching between versions of those plugins at a local or global level has saved me massive amounts of time compared to alternatives.

If you've never set up asdf before, follow the instructions here to get it set up. For reference, I use fish for my shell, so I installed asdf using the "Fish & Git" section.

Once you have asdf on your machine, the next step is to add the plugins you need for your project. Plugins are the actual tools that you want to manage the versions of, like NodeJS, Python, Ruby, etc. For the purposes here, I'll start with adding the plugin for Python:

asdf plugin-add python

Once you have added a plugin to asdf, you're ready to install various versions of that plugin. Since we just installed Python, we can install the version we want:

asdf install python 3.12.4
# OR if we want to just use whatever the latest version is
asdf install python latest

Once the version you want is installed, you can tell asdf to use that version in the current directory by running:

asdf local python 3.12.4
# OR 
asdf local python latest

depending on which version of python you installed.

The Dependency Manager #

In the past, I just used pip install and requirements file(s) to handle most of this. I knew of other options, like pipx or pipenv, but I still have never tried using them. I was more interested in finding a dependency manager that did these things in a significantly different way than what I was used to with pip.

Therefore, I wanted to find something that felt similar to bundler for Ruby. Luckily, very early on in my journey here, I found PDM.

Upon reading what PDM did, I immediately decided to try it out and get a feel for what it offered. Some key notes for me that piqued my interest:

  • Lockfile support
  • Can run scripts in the "PDM environment"
    • pdm run flask run -p 3000 executes the normal flask run -p 3000 command within the context of your installed packages with PDM.
    • In other words, it adheres to PEP 582 and allows you to run project commands without needing to be in a virtual environment, which to me is a big plus.
  • Similar commands to bundler
    • pdm run => bundle exec
    • pdm install => bundle install
    • pdm add <package> => bundle add <gem-name>
      • Note: My workflow was almost always to just add gem <gem-name> to the Gemfile rather than using bundle add, but there is no direct 1:1 equivalent of a Gemfile with PDM.

Installing PDM #

PDM has its own asdf plugin, so let's just use that here as well! Running:

asdf plugin-add pdm

adds the plugin itself to asdf, and running:

asdf install pdm latest 
# can replace 'latest' with a specific version number here too

installs the latest version of PDM. Finally, set the local version with:

asdf local pdm latest
Side note about asdf local
  asdf local creates a .tool-versions file (if it doesn't already exist) in the current working directory, and appends the plugin and version number to it. At this point, the directory in which you ran asdf local python 3.12.4 and asdf local pdm latest should have that .tool-versions file, and the contents should be a line each for Python and PDM with their associated version numbers. This way, if someone else pulls down your project, they can just run asdf install and it will install the versions of those plugins, assuming the user has the necessary plugins added themselves.

Now that we have PDM and Python set up, we're ready to use PDM to install whichever packages we need. For simplicity, let's set up a simple Flask app:

pdm add flask flask-sqlalchemy flask-htmx

This line adds Flask, Flask-SQLAlchemy and Flask HTMX. Flask is a web application framework, Flask-SQLAlchemy adds SQLAlchemy and its ORM, and HTMX builds on top of HTML to allow you to write more powerful HTML where you'd otherwise need some JS. Side note, but HTMX is really cool. If you haven't used it before, give it a go! I'm even a part of the exclusive group of HTMX CEOs.

Linting and Formatting #

Finally, I wanted to find a way to avoid pulling in multiple packages (commonly, Black, Flake8 and isort) to handle linting and formatting, which felt to me like it could be the job of one tool.

Pretty quickly I was able to find Ruff which did everything I wanted it to, along with being really fast (thanks Rust ????).

First things first, we need to install Ruff. Since it's a Python package, we can do it using PDM:

pdm add ruff

Once it's installed, we can use ruff check and ruff format to lint and format, respectively. Note that since we installed via PDM, we need to prepend those ruff calls with pdm run:

pdm run ruff check --fix

This runs the linter and fixes any issues found (if they are automatically fixable). The linter can also be run in --watch mode:

pdm run ruff check --watch

which re-lints on every saved change and tells you of any new errors it finds.

The Ruff formatter is similar to use:

pdm run ruff format

which will automatically fix any formatting issues that it finds and can fix. If you want to use this in CI (which you should), you can use the --check flag that will instead exit with a non-zero status code, rather than actually formatting the files:

pdm run ruff format --check

Bringing it all together #

Working with projects set up this way is much easier than how I used to do it. Using tools like asdf, PDM, and Ruff rather than pyenv, pip, and Black/Flake8/isort make both setting up projects and pulling down/installing existing projects more straightforward. I hope the contents of this article are helpful to anyone interested in setting up Python projects in a similar way.




and

5 Ways to Make Your Next In-Person All-Hands Event Worthwhile

In recent years, as distributed work has become the norm, all-hands trips have become more commonplace. Even companies that have always done them now see their importance in a new light. They are expensive and demanding for everyone involved, but when they’re done well they can be incredibly impactful. Are you bringing your team together and feeling the stress of getting it right? Designing the time together takes strategic thinking, logistics planning, and the confidence to ask everyone to contribute to the event’s success.

Last week, we came together to connect, celebrate each other, and look forward to a bright future in our quarterly all-hands event we call Third Third Thursday or TTT. It was our first in-person all-hands event since May 2023. At Viget, we actively build our culture together. The responsibility doesn’t fall on just a few individuals' shoulders. Our events are successful because individuals show up, speak up, listen, and honor the limited time we have together in-person. This is evident in the chats over breakfast pastries, the all-in approach to competitions breakout sessions, and the peer talks and discussions throughout our events. This connection and camaraderie doesn’t just happen, we work hard to make it happen. 

Reflecting on last week’s event (and 7 years of other events), I want to share these five key planning tips to help you craft a successful event. 

Leverage your physical space to help achieve your event goals. This fall we had a smaller number of attendees and we wanted to encourage discussion throughout the event so we set up the room in large U shape to foster conversation. We also made the most of our exterior space, which featured a fire escape staircase, by hosting an egg drop competition. 

Over communicate, especially around when team members can expect breaks and when they can expect to be fed. As the event lead, I share my phone number multiple times ahead of and throughout the event. We also make sure to communicate in dedicated channels, including an event Slack channel for all attendees, a behind-the-scenes Slack channel for presenters only, and an event website. We share important key information like bus schedules and flight confirmations via email. In advance of the big day, we shared this slide with our team to both inform and inspire. 

Leave room for unstructured time and connection. Over the years we learned that some of the best discussions and a-ha moments come from chats over a meal or a snack during the “unplanned” parts of the event. Now we plan ahead for these, leaving ample time for unplanned, impromptu a-has. We think about airport Uber rides, charter bus rides, morning runs, evening board games, or late-night drinks and look for ways to grease the wheels of serendipity. This was especially important for us this fall since it had been so long since the last time we all gathered together

Look for ways to celebrate what is unique to your company. One pretty fantastic thing that comes with a quarter century in business is the community we’ve built along the way. We make a point to keep up with previous Viget employees who we proudly call our alumni. We love our alumni and enjoy recognizing their contributions to Viget. One way we do so is with annual picnics, which we host when the most current Viget employees are in town for an event. These picnics are often a great mix of reconnections and networking.

Find ways to surprise and delight. Think big and small. For example, this fall we put colored pencils and butcher block paper out during our meeting time. Our autumnal decor included blue(ish) and orange pumpkins (Viget colors!). We made a subtle callout to an internal project by serving french fries as our afternoon snack. And we surprised our team with Unreal treats for their bus rides home. The treats served as dessert and a means for us to introduce our next in-person event, our spring TTT, aka Viget25. 

Some companies advocate for all-hand retreats to be social and relaxing, but not “productive.” They tout a meeting-free event. At Viget, we see the meeting time as crucial because that is when peers share knowledge, reflect on their recent work, showcase client impact, and highlight new ideas and personal growth. Similarly, our co-founders reflect on Viget’s performance and share thoughts on the upcoming business direction. The meeting sparks genuine inspiration and creates a time for us to remember what brought us to Viget in the first place – to build world class tech in support of our clients. 

On the heels of a successful gathering, it’s time to start planning the next one. For our next event we’ll head to the Allegheny Mountains where we’ll leverage a unique and memorable venue, offer plenty of unstructured time for connection (with each other and nature), loop in alumni where appropriate, plan inspiring meeting content, enjoy many meals and treats together, and of course find ways to surprise and delight. 

Work-related travel isn't for everyone, but if you know someone who appreciates the idea of dedicated time to connect with and celebrate their peers, we’re hiring. And to all the culture champions and event planners out there, good luck with your next all-hands! 




and

The Rise, And Fall, Of Adobe?

This post: The Rise, And Fall, Of Adobe? was first published on Beyond Photo Tips by Susheel Chandradhas

Many of us use Adobe tools regularly. I’ve used them since 1995, and I still do. But there was a time, a few years ago, when I was very frustrated with Adobe, and that I moved away from the Creative Cloud ecosystem. In the video coming up, Jazza explains why Adobe is so widely used […]

This post: The Rise, And Fall, Of Adobe? was first published on Beyond Photo Tips




and

RIP a Livecast #637 – 24 and Stupid

We kicked things off by talking about the new member of Noa's family. We then discussed the MTV Video Music Awards, and Madonna's "controversial" look. We also discussed Megan Fox […]



  • RIP a Livecast

and

RIP a Livecast #645 – Backstory and Frontstory

We talk about David Ellefson's absurd quote comparing him to Jesus. Speaking of Jesus, we check in with Marilyn Manson's Christianity. We discuss the latest NFT craze, Seinfeld, the Golden […]