|
BLOG
|
It's March in Kansas. Tornado season is ramping up. And if you've worked in emergency management or public safety GIS, you know the drill — when something hits, people need tools now. So I tried an experiment: How fast can I go from zero to a working, mobile-friendly field assessment app using AI and the ArcGIS Maps SDK for JavaScript 5.0? The answer surprised me. The Scenario This is a fictional scenario used to demonstrate the workflow — no actual emergency occurred. A tornado rips through the county. Fire and rescue need to go door to door checking on residents and assessing damage. They need a map application — on their phone — where they can tap a parcel, flag injuries, structural damage, or trapped persons, add comments, and submit. The data needs to flow back to ArcGIS Online in real time so the office has a live picture of response progress. Full walkthrough: from ArcGIS Online setup to working field app. The Tools ◈ ArcGIS Online — hosted feature layers ◈ Claude AI — Anthropic's AI assistant ◈ ArcGIS Maps SDK for JavaScript 5.0 ◈ A web server — IIS in my case, but any will do. That's it. No frameworks, no build tools, no npm install. One HTML file. The Process I created a hosted feature layer in ArcGIS Online with the important fields for person and property assessment, then opened Claude, attached two example applications I'd previously built with SDK 5.0 (since the version is brand new and examples help the AI produce better code), described the scenario, and let it build. Did it work perfectly on the first try? No — and I left that in the video on purpose. Claude hit a script loading order issue, a geometry type mismatch, and a URL that needed a layer index appended. But each time, I pasted the error back in and Claude diagnosed and fixed it within seconds. The entire build-and-debug cycle took roughly 30 minutes. The result is a dark-themed emergency assessment app with aerial basemap imagery, a tornado path overlay, property search with wildcard matching, a responsive sidebar assessment form, and real-time data submission to ArcGIS Online — all in a single index.html file I dropped into an IIS folder. What I Learned Give AI examples. SDK 5.0 is new enough that Claude benefits from seeing real working code in the patterns you want. Attaching previous projects made a big difference in output quality. Linking to the SDK 5.0 documentation would likely help even more. Expect iteration, not magic. AI isn't going to hand you a perfect app on the first prompt. But it compresses what used to be hours of Stack Overflow and documentation hunting into a rapid conversation. The more you use it, the better your prompts get — and the better the results. Save your projects. The Claude project with all the prompts, examples, and conversation history is saved. Next time a similar scenario comes up — a flood, a wildfire, a hazmat incident — you have a template ready. You're not starting from scratch. Keep it real. This video intentionally shows the messy parts — the errors, the fixes, the rough edges. I wanted to showcase real-time capability, not sell a fantasy. There's still cleanup to do (centering, polish, etc.), but in an emergency, good enough right now beats perfect next week. What This Means for GIS Professionals AI isn't replacing us. It's an accelerator. You still need to know your data, your services, your projections, your deployment. What changes is the speed at which you can go from "I need this" to "here it is." And for emergency response, that speed saves lives. I'd love to see what others build with this approach. Drop a comment below or share your own experiments. And if you're curious about the new AI components in SDK 5.0 — that's a whole other rabbit hole worth exploring. Stay safe out there.
... View more
03-18-2026
12:15 PM
|
8
|
1
|
737
|
|
POST
|
const mapElement = document.getElementById("yourMap");
await mapElement.componentOnReady();
mapElement.center = [-95.38, 39.23];
// Set scale at the view.
const view = mapElement.view;
view.scale = 560000; OR mapElement.extent = {
xmin: -95.50,
ymin: 39.15,
xmax: -95.20,
ymax: 39.35,
spatialReference: { wkid: 4326 }
} ALSO const [Extent] = await $arcgis.import([
"@arcgis/core/geometry/Extent"
]);
const mapElement = document.getElementById("myMap");
await mapElement.componentOnReady();
mapElement.extent = new Extent({
xmin: -95.50,
ymin: 39.15,
xmax: -95.20,
ymax: 39.35,
spatialReference: { wkid: 4326 }
}); goTo await mapElement.goTo({
xmin: -95.50,
ymin: 39.15,
xmax: -95.20,
ymax: 39.35,
spatialReference: { wkid: 4326 }
}); OR goTo const view = mapElement.view;
view.goTo({
xmin: -95.50,
ymin: 39.15,
xmax: -95.20,
ymax: 39.35,
spatialReference: { wkid: 4326 }
});
... View more
03-11-2026
07:51 AM
|
1
|
0
|
595
|
|
POST
|
Change the widget-stack z-index to auto, this basically isolates it from it's children. Then you can independently move the popover children. .widget-stack {
position: relative;
top: 50px;
display: flex;
flex-direction: column;
gap: 10px;
z-index: auto;
}
calcite-popover {
position: relative;
z-index: 5;
}
#table-placement {
position: absolute;
z-index: 1;
}
... View more
03-10-2026
07:33 AM
|
1
|
1
|
593
|
|
POST
|
The code pen you provided didn't have a number in the z-index for the #table-placement. #table-placement {
z-index: 1;
}
.widget-stack {
position: relative;
z-index: 10;
}
... View more
03-09-2026
02:19 PM
|
1
|
3
|
626
|
|
POST
|
That is strange. The reason I know how to change the attribution is because I ran into this. All I did for the screen shot was comment out the attribute code fix. But today it is not doing it. But, yes it is possible I did something I guess. On that note, the problem with changing the Calcite CSS is that it is too broad. Many different elements could use the same calcite, but when you only want to change it in one place...time to fight the shadow. Personally, I feel the the advantage of using the sdk vs experience builder is the ability to customize the design. In 5.0 it feels like way too much of that is being taken away.
... View more
03-06-2026
01:56 PM
|
1
|
2
|
426
|
|
POST
|
You don't have to change the width, I was just giving an example. Here is the js to get rid of the background. Take note that I am using dark mode in my design, so you will probably need to change that if you are not. await mapElement.componentOnReady();
const shadowRootAttr = mapElement.shadowRoot;
// Use an adopted stylesheet to beat the constructed stylesheet
const sheet = new CSSStyleSheet();
sheet.replaceSync(`
.esri-attribution.calcite-mode-dark {
--calcite-color-transparent-tint: transparent !important;
}
.esri-attribution {
width: auto !important;
max-width: 50% !important;
border-radius: 8px 8px 0 0 !important;
right: 0 !important;
left: auto !important;
}
`);
// Adopt it into the shadow root — appended last so it wins
shadowRootAttr.adoptedStyleSheets = [
...shadowRootAttr.adoptedStyleSheets,
sheet
];
... View more
03-06-2026
01:24 PM
|
0
|
0
|
1059
|
|
POST
|
Thanks, I was leaning that way. I was having trouble with some of Esri's sdk limitations or styling fighting me when I put it inside the shell/map. AI suggested I move non Esri components outside, and it fixed my issues. I think I figured all that out, honestly is was most likely a problem I created myself. I will trying moving them back into the shell and map. Does ESRI recommend to keep everything inside the shell or map?
... View more
03-06-2026
06:18 AM
|
0
|
0
|
418
|
|
POST
|
Esri Team, If we create custom div(s) is it best practice to keep them outside of the <calcite-shell> & <arcgis-map>? I ask because I have done this and have run into some users not being able to get to those outside div(s). When they try and click on one, the map is the focus, cannot reach the custom div. It is only a few so far, and unfortunately not any users I personally know so I am having trouble finding what is going on. My best guesses are that they have older windows, chrome, or their computers are not webGL compatible. Or possibly a stacking issue. Or should I keep them outside the <arcgis-map> but inside the <calcite-shell> perhaps? Could we possible get a slot="manual" so we can keep them in the map but have more control? Or am I missing something here? I isolated my custom div(s) from the shell for now...waiting to see if I get any more phone calls. #optionsDiv,
#toolbarDiv,
#topRightTools,
#searchHelpPanel {
pointer-events: auto;
isolation: isolate; /* forces new stacking context */
}
/* this does nothing */
arcgis-map:focus,
arcgis-map *:focus {
outline: none !important;
}
calcite-shell {
isolation: isolate;
}
... View more
03-05-2026
09:35 AM
|
0
|
2
|
483
|
|
POST
|
@Sage_Wall . The current attribution default in 5.0 is padded a bit from the bottom, where in the past it was hugging it. So now, with the lower slots and the attribution, they can overlap each other. It is shrinking the space to design by doing this.
... View more
03-05-2026
09:12 AM
|
1
|
4
|
1087
|
|
POST
|
Try this in your javascript. It worked for me. // ESRI attribution bar
const shadowRootAttr = mapElement.shadowRoot;
const attrStyle = document.createElement("style");
attrStyle.textContent = `
.esri-attribution {
width: auto !important;
max-width: 50% !important;
border-radius: 8px 8px 0 0 !important;
right: 0 !important;
left: auto !important;
background: rgba(0, 0, 0, 0.35) !important;
opacity: 0.7 !important;
}
`;
shadowRootAttr.appendChild(attrStyle); * I did a bit more digging. If you are using arcgis-map I don't think it can be reached in the shadow DOM through Calcite. I think it is because the Esri attribution is legally required, so be careful not to make it invisible.
... View more
03-05-2026
08:55 AM
|
1
|
2
|
1088
|
|
POST
|
Things I have observed that might help. Change the Calcite in the css where you can instead of fighting the dom. :root,
.calcite-mode-dark {
/* Typography */
--calcite-font-family: "Arial", system-ui, sans-serif;
--calcite-font-size--1: 12px;
--calcite-font-size--2: 13px;
} A lot of the shadow root is accessible, AI helps me a lot with this. // Shadow DOM styling
if (cc.shadowRoot) {
try {
const ccStyle = document.createElement("style");
ccStyle.textContent = `
:host {
border-radius: 8px !important;
overflow: hidden !important;
}
.root {
border-radius: 8px !important;
overflow: hidden !important;
}
.conversions-view {
padding: 6px !important;
}
.arcgis-coordinate-conversion__conversion-list,
.arcgis-coordinate-conversion__conversion-row {
width: 200px !important;
max-width: 200px !important;
}
.arcgis-coordinate-conversion {
background: #121826;
border-radius: 10px;
padding: 0px 0px;
width: 250px !important;
}
`;
cc.shadowRoot.appendChild(ccStyle);
} catch(e) {
console.warn("CC shadow style failed:", e);
}
}
... View more
03-04-2026
09:10 AM
|
1
|
0
|
1121
|
|
IDEA
|
@Noah-Sager In 5.0 setting the scale="s" for the sketch component does not work now. It is always bigger then the others. When you have time please look into it.
... View more
02-25-2026
12:38 PM
|
0
|
0
|
777
|
|
POST
|
Idea thread found here. Solution: // Configure only your parcel layer sources
// Custom getSuggestions bypasses Esri's 5.0 prefix-only behavior
// and restores the old LIKE '%value%' contains search
quickSearch.sources = [
{
layer: parcelLayer,
searchFields: ["PartyName_1"],
displayField: "PartyName_1",
name: "Search by Owner",
outFields: ["PartyName_1"],
exactMatch: false,
suggestionsEnabled: true,
getSuggestions: (params) => {
const query = parcelLayer.createQuery();
query.where = `PartyName_1 LIKE '%${params.suggestTerm.replace(/'/g, "''")}%'`;
query.outFields = ["PartyName_1"];
query.returnGeometry = false;
query.returnDistinctValues = true;
query.orderByFields = ["PartyName_1"];
query.num = 6;
return parcelLayer.queryFeatures(query).then(results => {
return results.features.map(f => ({
key: f.attributes.PartyName_1,
text: f.attributes.PartyName_1,
sourceIndex: params.sourceIndex
}));
});
}
},
{
layer: parcelLayer,
searchFields: ["PropertyAddress"],
displayField: "PropertyAddress",
name: "Search by Address",
outFields: ["PropertyAddress"],
exactMatch: false,
suggestionsEnabled: true,
getSuggestions: (params) => {
const query = parcelLayer.createQuery();
query.where = `PropertyAddress LIKE '%${params.suggestTerm.replace(/'/g, "''")}%'`;
query.outFields = ["PropertyAddress"];
query.returnGeometry = false;
query.returnDistinctValues = true;
query.orderByFields = ["PropertyAddress"];
query.num = 6;
return parcelLayer.queryFeatures(query).then(results => {
return results.features.map(f => ({
key: f.attributes.PropertyAddress,
text: f.attributes.PropertyAddress,
sourceIndex: params.sourceIndex
}));
});
}
},
{
layer: parcelLayer,
searchFields: ["QuickRefID_1"],
displayField: "QuickRefID_1",
name: "Search by Quick Reference ID",
outFields: ["QuickRefID_1"],
exactMatch: false,
suggestionsEnabled: true,
getSuggestions: (params) => {
const query = parcelLayer.createQuery();
query.where = `QuickRefID_1 LIKE '%${params.suggestTerm.replace(/'/g, "''")}%'`;
query.outFields = ["QuickRefID_1"];
query.returnGeometry = false;
query.returnDistinctValues = true;
query.orderByFields = ["QuickRefID_1"];
query.num = 6;
return parcelLayer.queryFeatures(query).then(results => {
return results.features.map(f => ({
key: f.attributes.QuickRefID_1,
text: f.attributes.QuickRefID_1,
sourceIndex: params.sourceIndex
}));
});
}
}
];
... View more
02-23-2026
09:28 AM
|
1
|
3
|
973
|
|
IDEA
|
In 4.34 & 5.0 the exactMatch: false → prefix only. In 4.21 it's like: LIKE '%ris%' This is a know behavior. See this thread.
... View more
02-23-2026
09:26 AM
|
2
|
1
|
366
|
|
POST
|
Is there a known fix or do I go create my own? Seems like it makes this component pretty useless for anything other then location. @NZGIS this is directly related to your problem, it is prefix only now.
... View more
02-23-2026
09:17 AM
|
1
|
1
|
981
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | 02-23-2026 09:28 AM | |
| 2 | 05-21-2026 09:21 AM | |
| 1 | 05-19-2026 02:25 PM | |
| 1 | 05-19-2026 08:11 AM | |
| 1 | 04-17-2026 01:08 PM |
| Online Status |
Offline
|
| Date Last Visited |
05-28-2026
06:06 AM
|