The essential news about content management systems and mobile technology. Powered by Perfect Publisher and XT Search for Algolia.
The News Site publishes posts to the following channels: Facebook, Instagram, Twitter, Telegram, Web Push, Bluesky, and Blogger.
On September 27th, Google published version 117 of the Android Webview to general availability. After this release, we were made aware that some Capacitor applications were reporting erroneous behavior, including hanging on application load. After investigating the changes in this release of the Webview, we identified that a change was made to the History API in this version that affects Capacitor applications using a custom scheme on Android. The result of this change is that custom schemes on Android can no longer change the URL path, which may prevent your application from routing correctly, making it appear to hang on load when routing fails.
Out of the box, Capacitor 5 and earlier runs your Android
application using the http
scheme, while Capacitor 6
will use https
. Both of these schemes are
still valid and are verified to continue to work with Webview 117.
If you are using either of these schemes explicitly, or are relying
on the default values provided by Capacitor, you should be
unaffected by this change and no action is required for your
application.
If you are providing a custom value to
androidScheme
in your Capacitor Configuration file
that is not http
or https
, it is likely
that you may be impacted by this change. We would highly encourage
you to test your application against Webview 117 to understand how
this change will affect your application.
For anyone impacted by this change, we would
strongly recommend that you change your
androidScheme
to use https
. Please note
that changing the scheme is the equivalent to shipping your
application on a different domain, which means any data stored in
cookies, localstorage, etc would no longer be accessible. Making
this change will align your application with best practices
recommended by both the Capacitor team as well as the Android
team.
If changing your scheme is not possible for your circumstances, the other alternative is to switch your application to use a hash-based routing strategy. While custom schemes are no longer allowed to change the URL path in Webview 117, they are still allowed to change query parameters or hash fragments. This approach can work as a temporary solution, however we do not recommend relying on this long-term.
We understand that these changes may necessitate some adjustments to your application, but they are essential to ensure the continued reliability and performance of your applications.
If you have any questions or require further assistance, please do not hesitate to reach out to our support team through the forum or Discord.
The post Capacitor Android customScheme issue with Chrome 117 appeared first on Ionic Blog.
Read more https://ionic.io/blog/capacitor-android-customscheme-issue-with-chrome-117
This is a guest post from Simon Grimm, Ionic Developer Expert and educator at the Ionic Academy, an online school with 70+ video courses focused entirely on building awesome mobile apps with Ionic and Capacitor!
What happens when users put your mobile app in the background? Can you still occasionally tap into the device’s resources to perform tasks? The answer is yes, and Capacitor makes it easy to do so now with the new Capacitor Background Runner.
In this tutorial, we will learn how to use the new Background Runner plugin to perform tasks such as fetching data, sending notifications, or even getting the user’s geolocation. We will also learn how to debug and trigger background tasks directly from Xcode.
You can also find the full source code on GitHub. But before we get into the plugin, let’s first take a look at what background tasks are and why we need them.
When building mobile apps, we often have to deal with tasks that should run in the background, like checking for new emails, updating chat messages, or syncing data. In the past, this was only possible with native code, but now we can do it with Capacitor as well.
The idea is to define a task that you will run in the background and either dispatch it manually from your code or let the OS trigger it automatically on a given interval.
However, the second option proves very challenging in reality. Because even if you tell iOS to perform your background task every 5 minutes, your code will most likely get executed every 2 hours.
This is because iOS will try to optimize the battery life of the device and only run background tasks when it thinks it’s a good time to do so. This is why you should never rely on background tasks to run at a specific time.
However, you can still use this to update your app’s data before a user opens it again or to send a local notification to the user.
It can also offload some work from the main thread to the background thread – and we will see all of that in action.
If you want to learn more about this, check out the Capacitor Livestream about Background Tasks!
To get started, bring up a terminal, create a new Ionic app, and install the Capacitor Background Runner:
ionic start backgroundApp blank --type angular
cd ./backgroundApp
npm install @capacitor/background-runner
ionic cap add ios
ionic cap add android
Next, we need to add a file that holds our background tasks, so
simply create one at src/app/runners/runner.js
and
leave it empty for the moment. To make sure this file is copied
when building the app, we need to bring up the angular.json and
include it in the assets array like this:
{
"projects": {
"app": {
"architect": {
"build": {
"options": {
"assets": [
{
"glob": "**/*",
"input": "src/assets",
"output": "assets"
},
{
"glob": "**/*.svg",
"input": "node_modules/ionicons/dist/ionicons/svg",
"output": "./svg"
},
{
"glob": "runner.js",
"input": "src/app/runners",
"output": "./runners"
}
]
}
}
}
}
}
}
Now we also need to specify some information for Capacitor so
the plugin can pick up the file and eventually an event that we can
trigger from our code. To do so, open the
capacitor.config.ts
and change it like this:
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.capacitor.background',
appName: 'backgroundApp',
webDir: 'www',
server: {
androidScheme: 'https',
},
plugins: {
BackgroundRunner: {
label: 'com.capacitor.background.check',
src: 'runners/runner.js',
event: 'checkIn',
repeat: true,
interval: 30,
autoStart: true,
},
},
};
export default config;
The most important part is that the plugin has a similar label to your appId! This is important because otherwise, the plugin won’t be able to find your file or execute the task. The time we specify here is more a friendly request to the OS as mentioned before, we are not guaranteed to get executed at this time.
To make sure our app can run in the background, we need to add some configuration to our native apps as well.
For iOS we first need to update the
ios/App/App/AppDelegate.swift
file and change one
function and add another:
import UIKit
import BackgroundTasks
import Capacitor
import CapacitorBackgroundRunner
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
BackgroundRunnerPlugin.registerBackgroundTask()
BackgroundRunnerPlugin.handleApplicationDidFinishLaunching(launchOptions: launchOptions)
return true
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
print("Received remote notification")
BackgroundRunnerPlugin.dispatchEvent(event: "remoteNotification", eventArgs: userInfo) { result in
switch result {
case .success:
completionHandler(.newData)
case .failure:
completionHandler(.failed)
}
}
}
}
Additionally, we need to enable the background capability and different modes that our app will use from the background:
Select the modes as you see fit, but for our example, these are the ones we need:
Now we also need to include some permissions in the
ios/App/App/Info.plist
for geolocation and also add
the BGTaskSchedulerPermittedIdentifiers
if it wasn’t
added before and use the key of your background task:
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.capacitor.background.check</string>
</array>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>$(PRODUCT_NAME) uses location services to track your location.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>$(PRODUCT_NAME) uses location services to track your location.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>$(PRODUCT_NAME) uses location services to track your location.</string>
This did not happen automatically for me, so check your file for that entry.
For Android we first need to add some permissions as well, so
open up the android/app/src/main/AndroidManifest.xml
and add the following:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-feature android:name="android.hardware.location.gps" />
There are no other changes needed for Android, so we are good to go!
Let’s start with an easy example and simply save a value to the CapacitorKV store. This is like the Capacitor Preferences plugin and is directly available to our Background Runner. To do so, open up the src/app/runners/runner.js and add the following code:
// Save a value to the Capacitor KV store
addEventListener('testSave', async (resolve, reject, args) => {
try {
CapacitorKV.set('foo', 'my bar 42');
resolve();
} catch (err) {
console.error(err);
reject(err);
}
});
// Get a value from the Capacitor KV store
addEventListener('testLoad', async (resolve, reject, args) => {
try {
const value = CapacitorKV.get('foo');
resolve(value);
} catch (err) {
console.error(err);
reject(err);
}
});
This is a very simple example, but it shows how we can use the
Capacitor API directly from our background task. Now that we have
two events defined, we can call them directly from our app! For
this, bring up the src/app/home/home.page.ts
and first
request permissions for our background runner and then call the
dispatchEvent()
method to trigger the event:
import { Component } from '@angular/core';
import { BackgroundRunner } from '@capacitor/background-runner';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
user: any = null;
constructor() {
this.init();
} // Request permissions for background tasks
async init() {
try {
const permissions = await BackgroundRunner.requestPermissions({
apis: ['notifications', 'geolocation'],
});
console.log('permissions', permissions);
} catch (err) {
console.log(`ERROR: ${err}`);
}
} // Test the KV Store
async testSave() {
const result = await BackgroundRunner.dispatchEvent({
label: 'com.capacitor.background.check',
event: 'testSave',
details: {},
});
console.log('save result', result);
}
async testLoad() {
const result = await BackgroundRunner.dispatchEvent({
label: 'com.capacitor.background.check',
event: 'testLoad',
details: {},
});
console.log('load result', result);
}
}
Now simply call those functions with some buttons and check your logs when you run the app on a real device – you should see the values being saved and loaded!
The real power of background tasks comes from the fact that we
can offload some work from our app, or periodically update some
data. Let’s now add a fetch call that returns data, and another
call to the CapacitorNotifications
plugin to schedule
a local notification.
Bring up the src/app/runners/runner.js
again and
add:
// Make a fetch request to the randomuser API and return first user
addEventListener('fetchTest', async (resolve, reject, args) => {
try {
const res = await fetch('https://randomuser.me/api/');
if (!res.ok) {
throw new Error('Could not fetch user');
}
const result = await res.json();
resolve(result['results'][0]);
} catch (err) {
console.error(err);
reject(err);
}
});
// Trigger a local notification
addEventListener('notificationTest', async (resolve, reject, args) => {
try {
let scheduleDate = new Date();
scheduleDate.setSeconds(scheduleDate.getSeconds() + 5);
CapacitorNotifications.schedule([
{
id: 42,
title: 'Background Magic
Again we can simply include those events in our
src/app/home/home.page.ts
like this:
// Test the background fetch
async performBackgroundFetch() {
const result = await BackgroundRunner.dispatchEvent({
label: 'com.capacitor.background.check',
event: 'fetchTest',
details: {},
});
this.user = result;
};
// Schedule a notification from background
async scheduleNotification() {
await BackgroundRunner.dispatchEvent({
label: 'com.capacitor.background.check',
event: 'notificationTest',
details: {},
});
};
And finally, update your UI inside the
src/app/home/home.page.html
to show the buttons and an
item that we try to load from the background:
<ion-header>
<ion-toolbar>
<ion-title> Capacitor Background </ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-button (click)="performBackgroundFetch()" expand="full" >Perform Background Call</ion-button>
<ion-button (click)="scheduleNotification()" expand="full" >Schedule Local Notification</ion-button>
<ion-button (click)="testSave()" expand="full">Save values</ion-button>
<ion-button (click)="testLoad()" expand="full">Load values</ion-button>
<ion-button routerLink="/map" expand="full">Map</ion-button>
<ion-item *ngIf="user">
<ion-avatar slot="start">
<img [src]="user.picture.thumbnail" />
</ion-avatar>
<ion-label>
<h2>{{ user.name.first }} {{user.name.last}}</h2>
<p>{{ user.email }}</p>
</ion-label>
</ion-item>
</ion-content>
Try and trigger the background fetch!
We have now successfully triggered a background task that fetches data and returns it to our app – a huge milestone! You can now also try to schedule a notification and see it appear on your device.
All of this works flawlessly if you have configured everything correctly in the first step. But let’s take this another step further.
We could now combine our background check-in with the Capacitor Google Maps Plugin to capture the geolocation, store it and later show it on a map!
To do so, first install the plugin and create a new page:
ionic g page map
npm install @capacitor/google-maps
To configure the plugin you need to supply your Google Maps API
key inside the
android/app/src/main/AndroidManifest.xml
like
this:
<application ...>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOURAPIKEY"/>
</application>
For more information about the integration also check out my Capacitor Maps tutorial on the Ionic Academy! Now we can add two new functions to our runner file to save and load the check-ins:
// Save a time and location object in the Capacitor KV store
addEventListener('checkIn', async (resolve, reject, args) => {
try {
console.log('checkIn event fired');
const { value } = CapacitorKV.get('CHECKINS'); // Gather some data
const time = new Date().getTime();
const location = await CapacitorGeolocation.getCurrentPosition(); // Create an array of checkins
let checkinArr = [{ location, time }]; // Try to append our data to the existing array
try {
const parsedArr = JSON.parse(value);
checkinArr = [...parsedArr, { location, time }];
} catch (e) {
console.log('no checkins');
}
console.log(checkinArr); // Save the array
CapacitorKV.set('CHECKINS', JSON.stringify(checkinArr));
console.log('checkin saved'); // Resolve the event call
resolve();
} catch (err) {
console.error(err);
reject(err);
}
});
// Get all checkins from the Capacitor KV store
addEventListener('loadCheckins', (resolve, reject, args) => {
try {
const { value } = CapacitorKV.get('CHECKINS');
try {
const arr = JSON.parse(value);
resolve(arr);
} catch (e) {
resolve([]);
}
} catch (err) {
console.error(err);
reject([]);
}
});
We have previously defined in our Capacitor configuration that
the checkIn event will be automatically scheduled when our app goes
to the background – if you use different event names, make sure you
update your config. To finish the maps implementation, bring up the
src/app/map/map.module.ts
and include the
CUSTOM_ELEMENTS_SCHEMA
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { MapPageRoutingModule } from './map-routing.module';
import { MapPage } from './map.page';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@NgModule({
imports: [CommonModule, FormsModule, IonicModule, MapPageRoutingModule],
declarations: [MapPage],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class MapPageModule {}
Finally, sprinkle in some CSS inside the
src/app/map/map.page.scss
to make the map view visible
on native devices:
capacitor-google-map {
display: inline-block;
width: 100%;
height: 400px;
}
ion-content {
--background: none;
}
Now we can use the map in our page and also display a list with
the check-ins below it, so change your
src/app/map/map.page.html
to:
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button defaultHref="home"></ion-back-button>
</ion-buttons>
<ion-title>Map</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-button (click)="loadCheckins()" expand="full">Load Checkins</ion-button>
<capacitor-google-map #map></capacitor-google-map>
<ion-list>
<ion-item *ngFor="let checkin of checkins">
<ion-label>
<h2>{{ checkin.location?.longitude }} - {{ checkin.location?.latitude }}</h2>
<p>{{ checkin.time | date:'medium' }}</p>
</ion-label>
</ion-item>
</ion-list>
</ion-content>
Finally in the src/app/map/map.page.ts
we need to
initialize the map and then load the check-ins from the background
runner using dispatchEvent()
again.
When we have the data, we can add markers to the map and display them:
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
import { BackgroundRunner } from '@capacitor/background-runner';
import { GoogleMap, Marker } from '@capacitor/google-maps';
@Component({
selector: 'app-map',
templateUrl: './map.page.html',
styleUrls: ['./map.page.scss'],
})
export class MapPage implements AfterViewInit {
@ViewChild('map') mapRef!: ElementRef<HTMLElement>;
map!: GoogleMap;
checkins: any[] = [];
constructor() {}
ngAfterViewInit() {
this.createMap();
}
async createMap() {
this.map = await GoogleMap.create({
id: 'my-cool-map',
element: this.mapRef.nativeElement,
apiKey: 'YOURAPIKEY',
config: {
center: {
lat: 51.88,
lng: 7.6,
},
zoom: 8,
},
});
}
loadCheckins = async () => {
const result = (await BackgroundRunner.dispatchEvent({
label: 'com.capacitor.background.check',
event: 'loadCheckins',
details: {},
})) as any;
if (result) {
this.checkins = [];
Object.keys(result).forEach((key) => {
this.checkins.push(result[key]);
});
this.addMarkers();
}
};
async addMarkers() {
if (this.checkins.length === 0) {
return;
}
const markers: Marker[] = this.checkins.map((item) => {
return {
coordinate: {
lat: item.location.latitude,
lng: item.location.longitude,
},
title: item.time,
};
});
await this.map.addMarkers(markers);
}
}
Once your background task has run, you should see the markers on the map!
But what if it’s not running and you don’t want to wait until it’s executed?
For iOS, there is a handy trick to manually trigger your background tasks.
First, we will add a random print statement to the
BackgroundRunner
file in our pods target after the
BGTaskScheduler.shared.submit()
call.
Set a breakpoint to the left of that line of code, run your app, and slide it to the background – your breakpoint will be triggered.
Now you can run the following command in the log window where you see (lldb):
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.academy.runner.check"]
Replace the identifier with your own and hit enter.
This causes iOS to schedule and run your background task when the app continues, so let the app continue and you should see a log from your runner.
If you bring up the app again, you should also see the check-ins on the map!
Apparently, the only debugging option for Android I found was to use the App Inspector view in Android Studio, from which you can at least get a glimpse of your background job:
If you find a way to directly trigger the Android background task, let me know!
In this tutorial we have learned how to use the new Capacitor Background Runner plugin to perform tasks in the background, like fetching data, sending notifications, or even getting the user’s geolocation.
We have access to a limited set of APIs already, but we need to make sure we request the permissions upfront and configure our native apps correctly. To see all of them in action, also check out the official Capacitor Background Runner demo app!
Keep in mind the limitations of background tasks which are quite heavily restricted on both iOS and Android. But if you act within the boundaries of the operating systems, adding background tasks to your Ionic app with Capacitor is now possible and right at your fingertips!
The post How to Create Background Tasks in Ionic with Capacitor appeared first on Ionic Blog.
Read more https://ionic.io/blog/create-background-tasks-in-ionic-with-capacitor
G2, a peer-to-peer software marketplace and review site, awards companies that excel in various business areas – from ease of use to technical support to employee growth. In the Fall of 2023, G2 recognized Ionic with numerous awards across a slew of categories, highlighting how easy it is to get started and build performant apps with our mobile development framework.
With a steady influx of reviews, Ionic maintains a 4.4+ star overall rating (out of 5), making it one of the top mobile development frameworks across a number of categories. With 68% of reviews being 5 stars, we’re thrilled and honored to see how many developers are making the most of mobile development with Ionic.
For the second year in a row, Ionic has been named the most usable mobile framework. A product’s Usability score is calculated by G2’s proprietary algorithm that factors in real-user satisfaction ratings for a number of use-related review questions. Based on user adoption, ease of admin setup, and ease of use, Ionic Framework remains the simplest mobile development framework out there.
“Ionic takes the pain of cross-platform development and makes it simple and easy,” says a review from David M, a Lead Software Engineer. Jedidiah W, CEO of OpenForge, notes that his team has built over 50 apps with Ionic in the last 8 years, highlighting out-of-the-box performance and deployment as critical to getting apps to market faster and smoother than ever. Sean N, a Senior Consultant, remarks that “Ionic has been really great at getting web and native apps up and running in a short amount of time.”
Ionic also ranks first for Small-Business Implementation for Mobile Development Platforms, which looks at the process of buying and implementing software. Getting set up with Ionic is as easy as signing up and getting started.
We’re extremely proud of our product usability and consider user experience when making any development decisions. We’re thrilled that developers across various industries are able to get up and running quickly and easily with Ionic.
In evaluating 17 different mobile development frameworks, Ionic was ranked 2nd, making us a key leader in the category. Products within this category are ranked by customer satisfaction (based on user reviews) and market presence (based on market share, seller size, and social impact).
Products in the Leader quadrant are rated highly by G2 users and have substantial Market Presence scores. We’re thrilled to be in the company of other tech leaders, such as React Native, Bootstrap, Kotlin, and more.
Ionic also boasts a top Momentum score within the Mobile Development Frameworks category. We couldn’t achieve this ranking (or any of these) without our amazing community and their honest feedback. If you’ve ever used our products, please consider leaving a review on G2!
About G2
G2 is the largest and most trusted software marketplace, helping 5.5 million people every month make smarter software decisions based on authentic peer reviews. Their scoring is based on reviews gathered from their user community, as well as data aggregated from online sources and social networks.
About Ionic
Ionic is a leader in enterprise mobile app development, with nearly 150,000 apps published in the app stores worldwide. Thousands of enterprise customers are using Ionic to build mission-critical apps for their internal and external customers. Ionic is unique in that it takes a web-first approach, leveraging HTML, CSS, and Javascript to build high-quality iOS, Android, desktop, and Progressive Web Apps, all from a single codebase.
The post Ionic Remains the Most Usable Mobile Development Framework appeared first on Ionic Blog.
Read more https://ionic.io/blog/ionic-remains-the-most-usable-mobile-development-framework
We’re so excited to announce the release of Ionic 7.4 with updates to Datetime, Checkbox, Radio, Toggle, and more!
Here’s what’s new
In Ionic 7.1 we shipped new features for styling Datetime. We are excited to ship another Datetime styling feature in Ionic 7.4 too!
The new calendar-day
CSS Shadow Part allows
developers to style the individual day buttons in the calendar grid
layout. We also added active
, today
, and
disabled
Shadow Parts so developers can customize
these day buttons based on state.
ion-datetime::part(calendar-day) {
color: purple;
}
ion-datetime::part(calendar-day disabled) {
color: gray;
}
ion-datetime::part(calendar-day today) {
color: purple;
border: 1px solid purple;
}
ion-datetime::part(calendar-day active) {
background: purple;
color: white;
}
New “stacked” label placement styles have been added for the Checkbox, Radio, Range, and Toggle components. This feature allows the visible text label to appear stacked on top of the control. The “alignment” property was also added to handle how the label and control are aligned when stacked.
<ion-toggle
label-placement="stacked"
alignment="start"
>Enable Notifications</ion-toggle>
In previous versions of Ionic, asynchronously setting the
value
property on Datetime would not update the
Datetime view if the component was already visible. This was done
to avoid a scenario where the Datetime view updates while the user
is interacting with it. However, this caused an issue when the
Datetime’s value was loaded asynchronously such as from a remote
server because the view would not update.
In Ionic 7.4 this behavior was revised to always update the
Datetime view when the value
property changes. We have
also updated the Datetime documentation to note this behavior and
make developers aware that they should hide the Datetime until the
value is set to avoid the previously mentioned issue.
Custom page transition functions receive an options
payload that has information regarding the transition about to run.
The TransitionOptions
interface was exported to add
stronger types to that payload.
The getIonPageElement
function was also exported to
make it easy to select both the entering and leaving views’
.ion-page
elements.
Thanks to hoi4 for contributing this feature!
Developers can install Ionic 7.4 from the latest
tag on NPM:
# Ionic Angular apps
npm install @ionic/angular@latest
# Ionic React apps
npm install @ionic/react@latest @ionic/react-router@latest
# Ionic Vue apps
npm install @ionic/vue@latest @ionic/vue-router@latest
# All other Ionic apps
npm install @ionic/core@latest
Thanks to everyone who made this release possible. We’re immensely grateful for the community’s continued contributions to improving Ionic. We’ll see you soon for the next release!
The post Announcing Ionic 7.4 appeared first on Ionic Blog.
Read more https://ionic.io/blog/announcing-ionic-7-4
Bootstrap v5.3.2 is here with bug fixes, documentation improvements, and more follow-up enhancements for color modes. Keep reading for the highlights!
abs()
is
deprecated since Dart Sass v1.65.0. It resulted in a deprecation
warning when compiling Bootstrap with Dart Sass. This has been
fixed internally by changing the values passed to the
divide()
function. The divide()
function
has not been fixed itself so that we can keep supporting node-sass
cross-compatibility. In v6, this won’t be an issue as we plan to
drop support for node-sass.<mark>
color customization for color
modes.Head to https://getbootstrap.com for the latest. It’s also been pushed to npm:
npm i bootstrap@v5.3.2
Read the GitHub v5.3.2 changelog for a complete list of changes in this release.
Visit our Open Collective page or our team members’ GitHub profiles to help support the maintainers contributing to Bootstrap.
Read more https://blog.getbootstrap.com/2023/09/14/bootstrap-5-3-2/